Persistence
The scene-builder auto-saves your work to the browser's IndexedDB and localStorage. When you reopen the page, everything is restored exactly as you left it.
What Gets Saved
IndexedDB (debounced 500ms)
The sajou-scene-builder database has 8 object stores:
| Store | Content |
|---|---|
scene | Scene dimensions, background, layers, positions, routes, zones |
entities | Entity definitions (sprites, spritesheets, GIFs) |
choreographies | Choreography definitions, steps, timing |
wires | Patch bay wire connections |
bindings | Entity bindings to choreography commands |
timeline | Signal timeline state |
shaders | Shader definitions, sources, uniforms |
assets | Image files as ArrayBuffer (incremental save) |
Each store wraps its data in a versioned envelope { version: 1, data: ... }.
localStorage (debounced 300ms)
| Key | Content |
|---|---|
sajou:remote-sources | Remote signal source configs (URL, protocol, API key, model) |
sajou:editor-prefs | Panel layouts, grid settings, view mode, pipeline layout |
Not Persisted
These are rebuilt or re-discovered on each session:
- Undo/redo stack
- Local signal sources (re-discovered via local discovery)
- Connection status (all sources start
"disconnected") - Active selections
- Compositor state
Save Flow
State change (any store)
│
├── IndexedDB stores ──→ debounce 500ms ──→ dbPut("store", "current", { version: 1, data })
│
└── localStorage stores ──→ debounce 300ms ──→ localStorage.setItem(key, JSON.stringify(...))Multiple rapid changes within the debounce window collapse into a single write.
On page unload (beforeunload), all pending debounced saves are flushed immediately.
Restore Flow
restoreState() runs before the workspace is initialized:
- Check if
scenestore has data -- if not, this is a first launch - Restore scene state (dimensions, background, layers, etc.)
- Restore entity definitions
- Restore choreography state (clear selection)
- Restore wiring state (clear drag state)
- Restore binding state
- Restore signal timeline (clear selection)
- Restore shader state (clear selection, reset
playing: true) - Restore assets:
ArrayBuffer→File→URL.createObjectURL()→objectUrl - Restore remote sources from localStorage
- Restore editor preferences from localStorage
- Clear undo stack (restored state has no history)
Asset Persistence
Assets are saved incrementally -- only new paths not already in IndexedDB are written. Each asset is stored as:
{
name: string; // original filename
path: string; // unique asset path (key)
category: string; // "sprites" | "spritesheets" | "gifs"
format: AssetFormat; // "png" | "svg" | "webp" | "gif" | "jpeg"
buffer: ArrayBuffer; // raw file data
naturalWidth?: number;
naturalHeight?: number;
frameCount?: number;
detectedFps?: number;
}On restore, ArrayBuffer is reconstructed into a File object and a fresh objectUrl is created.
Force Persist
forcePersistAll() bypasses debouncing and writes all stores immediately. It's called after ZIP import to ensure the imported scene is fully saved.
New Scene
The "New" button (Ctrl+N) triggers newScene():
- Clear all IndexedDB stores (
dbClearAll()) - Remove localStorage keys
- Reset all in-memory stores to defaults
- Clear undo stack
- Re-discover local sources (
scanAndSyncLocal())
Key Files
| File | Role |
|---|---|
state/persistence-db.ts | IndexedDB wrapper (singleton connection, CRUD helpers) |
state/persistence.ts | Auto-save/restore orchestrator |