Scenes & prefabs
A scene is a JSON document (.vscn.json) describing a tree of entities plus environment
settings. The Zod schemas in packages/engine/src/scene/schema/ are the single source of
truth for the format — see the generated Scene Schema reference.
Loading a scene: three passes
Section titled “Loading a scene: three passes”loadScene(url, ctx) fetches and parses a scene (a failed fetch — 404, 500 — throws with
the URL and status rather than surfacing as a cryptic JSON parse error);
loadSceneFromData(data, ctx) skips the fetch. Either way the loader runs three passes (packages/engine/src/scene/scene-loader.ts):
- Spawn.
spawnEntityrecurses into each entity’s children and registers every id (including nested ones) in anid → Object3Dmap. - Joints.
createSceneJointslinks physics bodies together using that id map (a joint references its targets by id). - Init.
componentManager.initAll()runs every component’sinit()— now that all entities and joints exist, so aninit()can safely reference other entities.
loadScene returns a SceneInstance with an update(dt) to tick per frame and a
dispose() to tear everything down.
Defaults: editor/runtime parity
Section titled “Defaults: editor/runtime parity”packages/engine/src/scene/defaults.ts is the single source of default values shared by the
editor and the runtime, so what you preview in the editor is what the game renders.
Prefabs
Section titled “Prefabs”A prefab (.prefab.json) is a reusable entity template: { version, name, root }. A
scene entity references one via a prefab field. When an entity has both a prefab and its
own fields, mergePrefabInstance takes the prefab definition first, then applies the
per-instance overrides on top. The merge is per-field: transform is replaced wholesale
when the instance sets it (never blended field-by-field); other sections (mesh, material,
physics, shadow, animation, audio, particles, spline, render, …) are shallow-merged — the
instance’s fields override the prefab’s same-named fields, with a few nested sub-objects (e.g.
physics.collider, material.clearcoat) also deep-merged so a partial override doesn’t drop
the prefab’s sibling fields; components are deep-merged by name; joints are replaced
wholesale; and children are appended — so one prefab can have many customized instances.
The loader caches fetched prefab and material files.
Querying the scene at runtime
Section titled “Querying the scene at runtime”Helper queries walk the live tree:
queryByTag(root, tag)queryByName(root, name)queryByComponent(root, componentName)
Metadata (tags, components, the entity definition) is stored in each object’s userData.
See also
Section titled “See also”- Scene Schema reference — every field, generated from Zod.
- Entities & components — what
componentshydrate into. - Prefab Schema reference — the
.prefab.jsonformat. - Editor: scene I/O — how the editor writes scenes.