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.
Determinism — one obvious way
Section titled “Determinism — one obvious way”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.
Phase ordering is sacred
Section titled “Phase ordering is sacred”Every fixed tick runs systems and GameComponent.update() methods in a fixed order:
| # | Phase | What runs |
|---|---|---|
| 1 | input | Sample keyboard / mouse / gamepad. |
| 2 | prePhysics | Apply forces/intents to rigid bodies. |
| 3 | physics | Step the Rapier world. |
| 4 | postPhysics | Read physics back; sync transforms to Object3D; dispatch triggers. |
| 5 | gameLogic | General gameplay. Default phase for GameComponents. |
| 6 | animation | Advance animation graphs / mixers. |
| 7 | preRender | Camera/HUD fixups before drawing. |
| 8 | render | Draw the frame. |
A component declares its phase with a static field; data flows in one direction through the phases, so you never wonder “did this run before or after physics?”
Locality — one place per feature
Section titled “Locality — one place per feature”A feature lives in one place. The scene format, the Zod schema that validates it, and the
default values that the editor and runtime share are all single sources of truth in
packages/engine/src/scene/. Change the schema once and the editor inspector, JSON-Schema
autocomplete, and runtime validation all update together.
Fail-fast validation
Section titled “Fail-fast validation”Bad data should fail loudly at load time, not silently at frame 10,000. Unknown component
names throw when a scene loads; schema parsing rejects malformed fields. npm run validate-scenes checks every scene against the registry before you ship.
The Object3D-as-truth payoff
Section titled “The Object3D-as-truth payoff”Because the editor edits the very Object3Ds the runtime uses, two things fall out for free:
- Hot reload of behavior — swap a
GameComponent’s code and keep its live state. - Editor-edits-truth — what you see in the editor is what the game runs.
React is for HUD only
Section titled “React is for HUD only”UI is mounted with mountUI(ctx.uiContainer, <Component/>) and is for HUD/overlay
only — not gameplay objects. The uiContainer is pointer-events-transparent by default;
interactive elements opt back in. (The Visual UI Editor builds on this.)
- Foundations — put the model to work.
- Manual: the game loop & phases — the full phase reference.