Skip to content

Serialization of the scene #44

Open

Description

@lucasfelixs

Context

There is no built-in way to save or load a scene. Scene holds entities in a Map<EntityId, Entity>, which JSON.stringify turns into {}, so every app has to write its own conversion.

Serialization is also the base for persistence (localStorage, a backend), for copy/paste and for undo/redo snapshots (#45).


What changes

Two pure functions in core, plus a versioned JSON format:

// @spatial-kit/core
export interface SerializedScene {
  version: 1;
  size: { width: number; height: number };
  layers: string[];
  entities: Array<{
    id: string;
    layer: string;
    bounds: Bounds;
    data?: unknown;
    selectable?: boolean;
  }>;
}

export function serializeScene(scene: Scene): SerializedScene;
export function deserializeScene(input: SerializedScene): Scene;
  • Entities are written in insertion order. That order matters: hitTestPoint and rendering use it for z-order inside a layer, so a round trip must preserve it.
  • data is copied as is. The app is responsible for data being JSON-safe.
  • deserializeScene validates the basics (known version, entity layers exist in layers, numeric bounds) and throws a clear error otherwise.
  • Branded types are restored on the way in (as EntityId / as LayerId), so callers keep the type safety.

Loading into a running editor can use what exists today (create the core with the deserialized scene), or a follow-up core.loadScene(scene) that replaces entities and emits entities:changed. Discuss in the comments if you want to include it.


Affected files

File Change
packages/core/src/serialization.ts (new) serializeScene, deserializeScene, SerializedScene
packages/core/src/index.ts Export the new module
packages/core/README.md Document the format and the functions

Invariants to preserve

  • deserializeScene(serializeScene(scene)) deep-equals the original scene, including entity order.
  • The output is plain JSON: JSON.parse(JSON.stringify(x)) deep-equals x.
  • Pure functions, no DOM, no production dependencies.

Implementation checklist

  • serializeScene / deserializeScene with tests (round trip, order preserved, empty scene, invalid input, unknown version)
  • Exported from index.ts
  • README section with an example (save to localStorage and load back)
  • Changeset (minor for core)
  • Run pnpm typecheck && pnpm test && pnpm lint
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions