Scene Format Reference
Source of truth: tools/scene-builder/src/io/export-scene.ts and tools/scene-builder/src/types.ts
ZIP Structure
scene-export.zip
├── scene.json (required)
├── entities.json (required)
├── choreographies.json (optional)
├── shaders.json (optional)
└── assets/
├── sprites/ (static images)
├── spritesheets/ (animated spritesheets)
└── gifs/ (animated GIFs)Only assets actually referenced by entity visuals are included. Asset paths in entities.json are rewritten to ZIP-relative paths. Filename collisions are resolved by appending a numeric suffix.
scene.json
Version: 1
interface SceneExportJson {
version: 1;
dimensions: SceneDimensions;
background: SceneBackground;
layers: SceneLayer[];
entities: PlacedEntity[];
positions: ScenePosition[];
routes: SceneRoute[];
zoneTypes: ZoneTypeDef[];
zoneGrid: ZoneGrid;
lighting: LightingState;
particles: ParticleEmitterState[];
}dimensions
{ width: number; height: number }background
{ color: string } // CSS hex, rendered underneath all layerslayers
Ordered Z-groups. Content is placed on layers. Higher order renders on top.
{
id: string;
name: string;
order: number;
visible: boolean;
locked: boolean;
}entities (placed instances)
Each placed entity references an entity definition by entityId.
{
id: string; // instance ID ("peon-01")
entityId: string; // definition ID ("peon")
x: number;
y: number;
scale: number;
rotation: number; // degrees
layerId: string;
zIndex: number; // within layer
opacity: number; // 0-1
flipH: boolean;
flipV: boolean;
locked: boolean;
visible: boolean;
}positions
Semantic position markers referenced by choreographies.
{
id: string;
name: string; // used by choreographies ("forge", "spawnPoint")
x: number;
y: number;
color: string; // editor-only
entityBinding?: string;
typeHint: "spawn" | "waypoint" | "destination" | "generic";
}routes
Freeform vector paths that entities follow during choreographed animations. Minimum 2 points.
{
id: string;
name: string;
points: RoutePoint[]; // { x, y, cornerStyle: "sharp"|"smooth", tension?, name? }
style: "solid" | "dashed";
color: string;
bidirectional: boolean;
fromPositionId?: string;
toPositionId?: string;
}zoneTypes
Semantic zone type definitions for grid painting.
{
id: string;
name: string;
description: string; // for LLM/MCP context
color: string;
capacity: number;
}zoneGrid
Painted zone grid. Flat row-major array.
{
cellSize: number; // pixels per cell
cols: number;
rows: number;
cells: (string | null)[]; // zone type ID or null
}lighting
{
ambient: { intensity: number; color: string };
directional: {
enabled: boolean;
angle: number; // compass degrees (0=N, 90=E)
elevation: number; // degrees above horizon
color: string;
intensity: number;
};
sources: LightSourceState[]; // point lights
}Each point light source:
{
id: string;
x: number;
y: number;
color: string;
intensity: number;
radius: number;
flicker?: { speed: number; amount: number };
}particles
{
id: string;
x: number;
y: number;
sprite: string; // asset path ("" = default circle)
type: "radial" | "directional";
count: number;
lifetime: [number, number];
velocity: { x: [number, number]; y: [number, number] };
direction: { x: number; y: number };
speed: [number, number];
colorOverLife: string[]; // hex gradient stops
size: [number, number]; // [start, end] in scene pixels
glow: boolean;
}entities.json
Version: 1
{
version: 1;
entities: Record<string, EntityEntry>;
}Each EntityEntry:
{
id: string;
tags: string[];
displayWidth: number;
displayHeight: number;
fallbackColor: string;
defaults: EntityDefaults;
visual: EntityVisual; // discriminated union on visual.type
sounds?: Record<string, string>;
}Entity visual types
sprite -- static image:
{ type: "sprite"; source: string; sourceRect?: { x, y, w, h } }spritesheet -- animated frames:
{
type: "spritesheet";
source: string;
frameWidth: number;
frameHeight: number;
animations: Record<string, { frames: number[]; fps: number; loop?: boolean }>;
}gif -- animated GIF:
{ type: "gif"; source: string; fps?: number; loop?: boolean }Entity defaults
{
scale?: number;
anchor?: [number, number]; // normalized, [0.5, 1.0] = bottom-center
zIndex?: number;
opacity?: number;
flat?: boolean; // lies flat on ground in isometric view
}choreographies.json
Version: 1
{
version: 1;
choreographies: ChoreographyDef[];
wires: WireConnection[];
bindings: EntityBinding[];
}Note: signal-to-signal-type wires (fromZone === "signal") are excluded on export because signal sources are session-ephemeral.
shaders.json
Version: 1. Only included when shaders exist.
{
version: 1;
shaders: ShaderDef[];
}Each ShaderDef:
{
id: string;
name: string;
mode: "glsl";
vertexSource: string;
fragmentSource: string;
uniforms: ShaderUniformDef[];
objects: ShaderObjectDef[];
passes: number; // 1 = single-pass, 2+ = ping-pong feedback
bufferResolution: number; // 0 = match canvas
}Key Files
tools/scene-builder/src/io/export-scene.ts-- export logictools/scene-builder/src/io/import-scene.ts-- import logictools/scene-builder/src/types.ts-- all type definitions