Skip to main content

Spatial Runtime

A zero-dependency semantic drawing runtime for Python.

Spatial Runtime combines a persistent scene graph, geometric constraints, atomic editing, versioned documents, reactive rendering, and a local interactive Studio. Drawings are ordinary Python programs, while every object remains addressable through a stable semantic ID.

Use it to build diagrams, illustrations, generated graphics, and machine-editable scenes that retain their structure after rendering.

Why Spatial Runtime?

  • Semantic scenes — address objects by IDs such as hero.head, not by fragile array positions.
  • Structured geometry — preserve hierarchy, styles, anchors, relations, constraints, metadata, dependencies, roles, guides, and z-order.
  • Safe editing transactions — solve and validate before committing, with automatic rollback when an edit is invalid.
  • Multiple outputs — generate deterministic SVG, PNG, PDF, canvas, inspection, and feedback documents.
  • Reactive workflow — watch drawing files and their local imports, retain the last good render after an error, and avoid unchanged artifact writes.
  • Local Studio — inspect, select, drag, edit, preview, undo, and export a scene in the browser.
  • Zero runtime dependencies — install the package without pulling in a third-party runtime stack.

Spatial Runtime requires Python 3.11 or newer.

Installation

Install the package from PyPI:

python -m pip install spatial-runtime

Confirm that the command-line interface is available:

spatial --help

Quick start

Create drawing.py:

from spatial import Circle, Rect, Scene

scene = Scene(600, 400, id="portrait")
scene.group("hero")

scene.add(
    Circle(
        (300, 130),
        55,
        id="head",
        role="structural",
        style={"fill": "#ffd8ad", "stroke": "#17131f", "stroke_width": 4},
    ),
    parent="hero",
)
scene.add(
    Rect(
        245,
        190,
        110,
        150,
        id="body",
        role="structural",
        style={"fill": "#ffb454", "stroke": "#17131f", "stroke_width": 4},
    ),
    parent="hero",
)

scene.relate("attached", "hero.head", "hero.body", at="neck")
scene.constrain("hero.head", horizontally_aligned_with="hero.body")
scene.solve_constraints()

Run the complete construct, render, inspect, and feedback cycle:

spatial cycle drawing.py --out-dir out

Or open the scene in Studio:

spatial studio drawing.py --open

Semantic scenes

A scene is more than a list of drawing commands. Nodes keep their meaning and relationships throughout the drawing workflow:

head = scene["hero.head"]
head.anchor("neck", (300, 180))
head.set_lod(summary="circular face attached to the body")

scene.relate("attached", "hero.head", "hero.body", at="neck")
scene.constrain("hero.head", horizontally_aligned_with="hero.body")

Stable hierarchical IDs make scenes straightforward to inspect, patch, and modify from Python or an automated tool.

Atomic edits and validation

Scene.edit() treats a group of changes as one transaction. Spatial Runtime solves constraints and validates the scene before committing. Solver non-convergence or an error-level validation issue rejects the transaction, restores the previous state, and raises EditRejectedError.

from spatial import EditRejectedError

try:
    with scene.edit("move the head upward") as edit:
        scene["hero.head"].move(dy=-8)
except EditRejectedError as error:
    print(error.result.reason)
    print(error.result.solve_report)
    print(error.result.issues)
else:
    print(edit.result.diff)

Warnings, including out-of-canvas geometry, remain visible without rejecting a valid edit. Use allow_invalid=True when you deliberately need to commit an invalid state while retaining its complete diagnostics.

Undo, redo, and named snapshots are available during the active Python or Studio session.

Rendering and inspection

Render the same semantic scene to several targets:

scene.render_svg("render.svg")
scene.render_blueprint("blueprint.svg")
scene.render_raster("render.png", scale=2)
scene.render_pdf("render.pdf")
scene.save("scene.json")

print(scene.inspect("hero", detail="full"))
print(scene.inspect("hero", detail="full", structured=True))
print(scene.occupancy("hero"))
print(scene.validate())

Portable outputs use explicit, versioned document envelopes:

Document Schema
Scene snapshot spatial.scene
Declarative patch spatial.patch
Canvas commands spatial.canvas
Structured inspection spatial.inspection
Reactive feedback spatial.feedback

For example, Scene.save() writes a lossless snapshot of the current scene:

{
  "schema": "spatial.scene",
  "schema_version": 1,
  "scene": {
    "id": "portrait",
    "width": 600,
    "height": 400,
    "styles": {}
  },
  "nodes": [],
  "constraints": [],
  "relations": []
}

All built-in node types have exact codecs. Materialized custom Component subclasses load as FrozenComponent snapshots that retain their class provenance, layer metadata, hierarchy, and children. Custom Node subclasses can register their own codecs.

Packaged JSON Schemas are available through load_schema("scene"); use "patch", "canvas", "inspection", or "feedback" for the other document types.

Declarative patches

[!NOTE] The patch protocol is experimental and may evolve between releases.

Scene.apply_patch() applies a JSON-compatible set of operations atomically:

result = scene.apply_patch(
    {
        "schema": "spatial.patch",
        "schema_version": 1,
        "label": "Move the head upward",
        "operations": [
            {"op": "move", "target": "hero.head", "dx": 0, "dy": -8}
        ],
    }
)

Patches support:

  • adding, removing, and reparenting nodes;
  • moving, positioning, scaling, rotating, and editing handles;
  • changing styles, metadata, visibility, roles, z-order, and anchors; and
  • adding or removing relations and constraints.

Operations run in order inside one transaction. An unknown operation, malformed payload, missing target, or rejected edit rolls back the entire patch. dry_run=True evaluates the patch on a clone and returns its projected diff, diagnostics, and canvas without changing the live scene.

Reactive rendering

Watch a drawing and rebuild when its source or local Python imports change:

spatial watch drawing.py --out-dir out --debounce 0.15

The reactive runner:

  • waits for file changes to settle before rebuilding;
  • reloads changed local modules;
  • retains the last valid scene and artifacts after execution errors;
  • reports failures as structured feedback and continues watching;
  • writes artifacts atomically; and
  • skips filesystem writes when the generated bytes are unchanged.

SVG fragments and canvas commands are cached by normalized node state. Reloading canonical Python source starts a fresh session and clears undo/redo.

Local Studio

[!NOTE] Studio is experimental and intended for trusted local use.

spatial studio drawing.py --host 127.0.0.1 --port 8765 --open

Studio includes:

  • a searchable semantic hierarchy with role and layer filters;
  • a zoomable SVG viewport with click selection and drag-to-move;
  • geometry, style, anchor, relation, constraint, metadata, and dependency controls;
  • guide, blueprint, bounds, anchor, and role overlays;
  • editable semantic handles and dry-run previews;
  • undo, redo, reload, and JSON snapshot export; and
  • structural diffs, changed-node highlighting, pixel-diff overlays, warnings, and solver status.

Studio edits only the in-memory scene and never rewrites the Python drawing. Reloading the source discards session edits and starts a new undo/redo history.

Studio has no authentication or security boundary. Drawing files execute as fully trusted Python, so run only code you trust and keep Studio bound to 127.0.0.1 unless you intentionally choose otherwise.

Source and history model

Python source is authoritative. Scene JSON captures the complete current scene state for interchange, inspection, and export, but it does not contain snapshots or undo/redo history.

Use Git for durable source history. Spatial Runtime intentionally does not add a sandbox, permission system, authentication layer, or revision-control layer around drawing files.

Documentation

The examples directory contains complete scenes demonstrating primitives, components, constraints, patterns, fields, surfaces, skeletons, perspective, rendering, and inspection.

Development

Clone the repository, then install the development tools:

python -m pip install -e ".[dev]"

Run the main checks:

python -m compileall -q src
ruff check src tests
mypy
coverage run -m unittest discover -s tests -v
coverage report
node --test tests/js/studio.test.mjs

The release suite covers persistence round trips, transaction rollback, patch atomicity, deterministic renderer documents, reactive recovery, Studio endpoints, browser behavior, and performance at 100-, 1,000-, and 10,000-node scene sizes.

Project status

Spatial Runtime 2.0.0 is beta software. The core scene, persistence, transaction, validation, rendering, inspection, and reactive APIs are stable. The patch protocol and Studio are experimental.

Released under the MIT License.

Release files for spatial-runtime 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for spatial-runtime 2.0.0
File Size Uploaded
spatial_runtime-2.0.0.tar.gz 94.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for spatial-runtime 2.0.0
File Interpreter ABI Platform
spatial_runtime-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 186.3 kB

Release files / spatial_runtime-2.0.0.tar.gz

Download URL spatial_runtime-2.0.0.tar.gz
Size 94.4 kB
Tags Source
SHA-256 checksum
How to use checksums
25d69b3b592e086ddf53c21981c87d781f08811d490e0d3d7833e7923b4649fc
BLAKE2b-256 checksum
How to use checksums
e8c9393d07c2d628af9d4542d0226f19f11261b25ff002bff9a676ffb57c0940
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release files / spatial_runtime-2.0.0-py3-none-any.whl

Download URL spatial_runtime-2.0.0-py3-none-any.whl
Size 91.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f8640d2d66258cf97fcab91dd794c939ae0272dab6feee4713c252e9fe4c6a29
BLAKE2b-256 checksum
How to use checksums
b43b5115f34a053ebf9674109bf7692291513dbad7d1bc00bf2c13dc37bb6f92
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page