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({
scenes: { MainScene },
canvas: {
width: 800,
height: 600,
mount: 'body',
},
clearColor: new Color(0x6495ed),
});
await app.start(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. canvas.mount places 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 type { RenderingContext, Seconds } from '@codexo/exojs';
import { Color, Graphics, Scene } 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: Seconds): void {
this._box.rotate(delta * 90);
}
public override draw(context: RenderingContext): void {
context.render(this.root);
}
}update(delta) advances state — here, rotating a box — and draw(context) renders it. delta 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('assets/hero.png'). The path’s extension tells the loader what type to produce, so no import of Texture is needed just to load it. The Loading and resources chapter covers the loader in detail.
Drop the public/ prefix in asset paths
public/ is the folder the dev server serves from, not part of the URL. A file at public/assets/hero.png is loaded as assets/hero.png — keeping the public/ prefix in the path gives you a 404 at runtime.
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.
