SceneLoader parses JSON scene files into entities. It's renderer-agnostic — same JSON, same loading, whether your renderer is Babylon, Three, or Mock.
Why a scene loader
Game levels are data, not code. Hard-coding entity creation makes:
- Iteration slow — change a position, rebuild the whole game
- Authoring hostile — non-programmers can't touch the level
- Save/load awkward — you've already invented half a serialization format ad-hoc
A scene loader fixes all three. Describe the world in JSON. Load it. Save mutations back to JSON. Reload anywhere.
The basics
The scene JSON shape
Top-level:
name(required) — identifier forinstantiateScene(name, world)entities(required) — map of entity-name → entity definition
Per-entity:
tags(optional) — string array applied viaentity.addTag()components(required) — map of component-name → component data- The component-name keys must match what you passed to
registerComponent
Registering components
The SceneLoader has to know which class corresponds to which name in the JSON. You tell it:
Arguments:
- The string name as it appears in JSON
- The
Componentsubclass - (Optional) The
Systemsubclass — gets auto-instantiated wheninstantiateSceneruns
Or in bulk:
What instantiateScene returns
entities—Map<string, Entity>keyed by the entity-name in the JSONsystems—System[]that were auto-created (one per registered Component-with-System that appeared in the scene). You still need to add these to the World.worldEntity— the entity whose name matchessceneData.worldEntity(used for global state like score-tracker components)
Loading from a URL
The SceneLoader can fetch directly:
loadSceneFromUrl is a thin wrapper around fetch + loadSceneFromData. No magic. If you need custom headers or a non-fetch transport (cached scenes from IndexedDB, scenes embedded in another bundle), call loadSceneFromData(data) directly.
Multiple scenes loaded at once
You can load many scenes and instantiate one at a time:
The loader stores parsed scene data by name. instantiateScene looks it up and builds entities.
Lazy component resolution
SceneLoader.registerComponent requires the Component class already imported — the SceneLoader doesn't know how to find it on disk. This is intentional: it keeps the loader renderer-agnostic and zero-dependency.
If you want lazy on-demand imports — "when the JSON references MeshPrimitive, only then pull in the MeshPrimitive module" — that lives in the ArcadeGame class in @babylonjsmarket/arcade. It wraps SceneLoader and adds a name → () => import(...) registry.
Errors
The SceneLoader emits events on its EventBus while loading:
A component name in the JSON that's not in the registry produces a warning in the console but doesn't throw — the entity is still created, missing components are silently skipped. Decide whether that's what you want for your project.
Where to next
- Testing — running scene-loaded games in vitest