tldraw-playground
Making state machines and UI shorthands talk
Visual statechart editor combining tldraw's infinite canvas with XState v5 state machines
A visual statechart editor combining tldraw’s infinite canvas with XState v5’s state machine runtime. The goal: bidirectional editing between visual diagrams, text notation, and executable state machines.
Current Status
Phase 1 MVP complete. The core interaction loop works: visual state nodes → XState machine → events → state changes → visual feedback. Single-user, no collaborative sync yet.
Demo shows a traffic light state machine (Red → Green → Yellow → Red). Click any state to advance the machine.
Architecture
Stack:
- Canvas: tldraw v2 with custom
StateNodeShape - State Runtime: XState v5 with
setup()pattern - Integration: React hooks (
useActor) for reactive updates - Testing: Vitest + jsdom (7 tests passing)
Data Flow:
User clicks canvas shape
↓
Send XState event (NEXT)
↓
XState actor updates state
↓
React hook subscribes to state change
↓
Update tldraw shape props (isActive)
↓
Visual feedback renders
Key Discoveries
Phase 1 revealed critical integration patterns not documented elsewhere:
tldraw Custom Shape Contract
Required methods: getGeometry(), component(), getDefaultProps(), indicator(). Shapes render via HTMLContainer (React components in HTML), not raw SVG. Type system must extend TLBaseShape<'shape-type', PropsInterface>.
XState v5 + React Integration
Create and start actor at module scope (singleton). useActor(actor) returns [snapshot, send] for subscription. Critical: actors MUST be started before component mount.
tldraw Editor Lifecycle
Use useRef<Editor | null> + onMount callback. Shape creation must happen in onMount or after editor available. Event handling: attach listeners to container DOM element, return cleanup function.
Roadmap
Why This Exists
Existing statechart tools are either:
- Text-only (XState visualizer) — no spatial reasoning
- Visual-only (draw.io, Miro) — not executable
- Full IDEs (Stately) — heavy, opinionated
This playground sits in between: a sketch-first tool that produces executable state machines. Inspired by sketch.systems.
Open Questions
- How to handle hierarchical/composite states visually?
- Best tree-diffing algorithm for bidirectional sync?
- Performance with 1000+ state nodes?