Serialization & prefabs
Serialize scenes to JSON and back, capture reusable prefabs, persist save slots with a key-value store, and register serializers for your own node types.
Serialization & prefabs
ExoJS can turn a scene-graph subtree into plain JSON and rebuild it later. The serializer captures data and structure — transforms, visuals, asset references, and the tree shape — not behaviour. Update logic, signal handlers, tweens, and systems live in your Scene / System code and are re-attached after a load. That boundary keeps the format small and matches the engine’s code-first design.
Use it for level/scene layouts, reusable prefabs, the structural half of save games, editor save/load, and snapshot tests.
Serializing a scene
Scene.serialize() returns a JSON-able descriptor; Scene.deserialize() rebuilds the scene’s root (and ui layer, if present) from one. Texture and other asset references resolve to their loader source keys, so the referenced assets must be loaded into the application’s loader before you deserialize.
import { Scene } from '@codexo/exojs';
declare const scene: Scene;
// Capture the current layout as plain JSON.
const data = scene.serialize();
const json = JSON.stringify(data);
// ...later, with the same assets pre-loaded, restore it.
scene.deserialize(JSON.parse(json));
A freshly-constructed node serializes to just its type tag — only non-default fields are written, so the JSON stays compact.
Prefabs
A Prefab captures a configured subtree once and stamps out independent copies — the data-driven counterpart to building objects in a code factory. There is no re-serialization per instance.
import { Prefab, type SceneNode } from '@codexo/exojs';
declare const coin: SceneNode;
const prefab = Prefab.from(coin);
for (let i = 0; i < 10; i++) {
const instance = prefab.instantiate();
instance.setPosition(i * 32, 0);
}
prefab.toJSON() and Prefab.fromJSON(descriptor) round-trip a prefab through disk or the network.
Persisting saves
The engine ships the save primitives, not a save system: Scene.serialize() captures a scene’s structural state, and a KeyValueStore persists it under a slot key. Slot naming, profiles, autosave, and metadata are game-specific, so pair this with your own game-state persistence (score, inventory, quest flags) for a complete save system — the serializer owns the world layout, your code owns the gameplay variables.
import { WebStorageStore, type Scene, type SerializedScene } from '@codexo/exojs';
declare const scene: Scene;
const store = new WebStorageStore(localStorage, { prefix: 'mygame:' });
await store.set('slot-1', scene.serialize()); // serialize + persist
const data = await store.get<SerializedScene>('slot-1'); // fetch
if (data) scene.deserialize(data); // restore
Choose the backend by capability: WebStorageStore for small synchronous JSON saves, IndexedDbKeyValueStore for large or binary saves, MemoryStore for tests. They share one KeyValueStore interface, so swapping is a one-line change.
Custom node types
Your own SceneNode subclasses become serializable by registering a serializer with registerSerializer. The framework writes the common transform/visual fields and the type tag; your serializer only handles its own fields.
import { registerSerializer, SceneNode } from '@codexo/exojs';
class Marker extends SceneNode {
public kind = 'spawn';
}
registerSerializer('Marker', Marker, {
write: node => ({ kind: node.kind }),
read: data => {
const marker = new Marker();
marker.kind = String(data.kind);
return marker;
},
});
Extension packages register their own node types the same way through the serializers binding on their Extension descriptor.
What is not captured
The format deliberately stops at structure:
- Behaviour — update logic, signal handlers, tweens, input bindings, systems. Re-attach these in code after a load.
- Live/heavy references — a node
maskthat points at another node, post-processfilters, aGeometryclip shape, and custom materials are skipped (with a one-time warning). Transform, tint, blend mode, frame, and asset references all round-trip. - Runtime-only assets — a
RenderTextureor other resource not loaded through theLoaderhas no source key, so the reference is omitted.
Every value the serializer writes is JSON-serialisable; caches, matrices, and dirty flags never appear in the output.