Skip to content

Design philosophy

vgai’s design is shaped by one north star: it should be a great engine for an AI (or a human) to author games in. That yields three priorities.

There should be one obvious place and one obvious way to do a thing. The engine avoids configurable indirection in favor of a single, predictable path. The clearest expression of this is phase ordering.

Every fixed simulation tick runs registered systems in a fixed order:

#PhaseWhat runs
1inputSample keyboard / mouse / gamepad.
2prePhysicsApply forces/intents to rigid bodies.
3physicsStep the Rapier world.
4postPhysicsRead physics back; sync transforms to Object3D; dispatch triggers.
5gameLogicGeneral gameplay systems.
6animationAdvance animation graphs / mixers.
7preRenderCamera/HUD fixups before drawing.
8renderDraw the frame.

Imperative systems register in one of these phases. R3F and Pixi components use their native frame hooks; the host keeps simulation and display-rate presentation explicit.

A feature lives with its owner. Spatial composition remains TS/TSX, models and materials remain glTF, React states remain portable CSF stories, and structured project data remains JSON with a colocated Zod or JSON Schema. The editor does not introduce a second scene model.

Bad data should fail loudly at load time, not silently at frame 10,000. Schema parsing rejects malformed fields and unknown manifest keys. npm run validate-assets content-parses every shipped asset before you ship.

Because the editor edits the very Object3Ds the runtime uses, two things fall out for free:

  • Native hot reload — React and the project’s bundler preserve state where their normal HMR semantics allow it.
  • Editor-edits-truth — what you see in the editor is what the game runs.

Every game UI is declared in vgai.project.json as a root with "adapter": "dom" and an entry. It mounts as a DOM layer in the same z-ordered host stack as Three.js and PixiJS canvases. JSX source is truth, so the editor hierarchy can inspect it and style/className edits write back to the JSX. There is no hidden HUD mount API or implicit UI container.

React reads live game state through the engine’s WorldProvider and useWorldState selectors (zero polling). React nodes are ordinary DOM/React source; gameplay behavior remains in the game root.