Project structure
Find your way around a create-exo-app project: the entry point, scenes, assets, and how main.ts wires an Application to a Scene.
Project structure
A create-exo-app project is a standard Vite + TypeScript app. The minimal template gives you the smallest layout that still separates startup from scene code:
my-game/
├─ index.html # mounts the app, loads src/main.ts
├─ package.json # @codexo/exojs + Vite scripts
├─ tsconfig.json # strict TypeScript config
├─ vite.config.ts # dev server and build config
├─ public/
│ └─ assets/ # static files served as-is (images, audio, fonts)
└─ src/
├─ main.ts # entry point: create the Application, start a Scene
└─ scenes/
└─ MainScene.ts # your first scene
Two files hold the code you will edit most: main.ts and the scene under src/scenes/.
The entry point
src/main.ts is where the app starts. It creates one Application, mounts its canvas, and starts a Scene:
import { Application, Color } from '@codexo/exojs';
import { MainScene } from './scenes/MainScene';
const app = new Application({
canvas: {
width: 800,
height: 600,
},
clearColor: Color.cornflowerBlue,
});
document.body.append(app.canvas);
app.start(new MainScene()); The application owns runtime configuration — canvas size, clear color, render backend, and the frame loop. app.start(scene) hands control to a scene and begins ticking. document.body.append(app.canvas) mounts the canvas; in a real layout you place it wherever your page needs it.
The scene
src/scenes/MainScene.ts is the scene main.ts starts. It sets up state in its constructor, updates it each frame, and draws it:
import { Color, Graphics, Scene } from '@codexo/exojs';
import type { RenderingContext, Time } from '@codexo/exojs';
export class MainScene extends Scene {
private readonly _box = new Graphics();
public constructor() {
super();
this._box.fillColor = Color.white;
this._box.drawRectangle(-40, -40, 80, 80);
this._box.setPosition(400, 300);
this.addChild(this._box);
}
public override update(delta: Time): void {
this._box.rotate(delta.seconds * 90);
}
public override draw(context: RenderingContext): void {
context.backend.clear();
context.render(this.root);
}
} update(delta) advances state — here, rotating a box — and draw(context) renders it. delta.seconds carries the elapsed time since the last frame, so motion stays frame-rate independent. The Your first scene chapter builds a scene like this from scratch.
Where assets go
Files under public/assets/ are served as-is and addressed by path at runtime — for example loader.load(Texture, { hero: 'assets/hero.png' }). The Loading and resources chapter covers the loader in detail.
Scripts
The template’s package.json defines the standard Vite scripts:
npm run dev # start the dev server with hot reload
npm run build # produce a production build in dist/
npm run preview # serve the production build locally
The other templates add more structure on top of this same shape: game-starter splits player logic into src/objects/ and adds a game-over scene, and audio-reactive adds an analyser-driven scene. The entry point and scene model stay the same.