mcp server
spatialboard
A React + TypeScript spatial canvas and node-board library for whiteboards, visual editors, and graph interfaces.
Set up
Runs on your computer. Pick your agent and operating system.
This project does not publish an install method we can turn into a config. Follow the README instead.
README
synced Oct 25, 2026SpatialBoard is a whiteboard, node-graph editor, presentation surface, and
LLM-readable board format in one MIT-licensed TypeScript library. All board
state lives in a SpatialEngine, with a React shell on top: you get an
infinite canvas with hand-drawn aesthetics out of the box, and a registry API
that turns that canvas into whatever your product needs — a diagramming tool,
a visual programming environment, a slide deck, or a board that AI agents can
read and write.
Feature highlights
- Whiteboarding, complete — pressure-aware freehand ink, rough hand-drawn shapes, sticky notes, rich text, images, and frames, with snapping, smart alignment guides, grouping, align/distribute, infinite pan/zoom, undo history, and a minimap.
- Custom nodes are the core API — everything on the canvas is a node type: a React component plus a declarative definition. The built-ins use the same public API you do.
- Typed data flow — nodes declare input/output ports and a pure
computefunction; the reactiveDataFlowEnginepropagates values through the wired graph, detects cycles, and badges live values on edges. - Presentations built in — frames double as slides, stepped through with
animated transitions (pan, fade, dissolve, zoom, fold, 3D cube). One call:
engine.enterPresentation(). - Collaboration-ready — transport-agnostic remote ops and live gesture awareness; wire it to any sync layer or CRDT. Storage stays your business.
- AI-native — budgeted board snapshots for LLM context, a programmatic drawing API, and SBD: a markdown-compatible board format that models both read and write.
- Headless entry —
spatialboard/engineimports without React or CSS, so the engine runs in tests, scripts, and agent tooling. - Interop — imports Excalidraw
.excalidrawlibshape libraries, and a built-in Mermaid importer turns flowcharts and sequence diagrams into editable nodes.
Quick start
npm install spatialboard
import { SpatialBoard, SpatialEngine } from "spatialboard";
import "spatialboard/style.css";
import { useMemo } from "react";
export default function App() {
const engine = useMemo(() => new SpatialEngine(), []);
return (
<div style={{ width: "100vw", height: "100vh" }}>
<SpatialBoard engine={engine} />
</div>
);
}
react / react-dom (^18 || ^19) are the only required peer
dependencies. Three entry points let you take exactly as much as you need:
| Entry | What you get |
|---|---|
spatialboard | The board component, the engine, and the built-in node types |
spatialboard/blocknote | Opt-in rich-text nodes (adds the BlockNote ^0.46 + Mantine ^8 peers) |
spatialboard/engine | The headless engine — no React, no CSS |
docs/getting-started.md covers the engine lifecycle, persistence, and the key props.
What you can build
A whiteboard. Freehand ink (perfect-freehand), rough hand-drawn shapes (roughjs), sticky notes, rich-text blocks (BlockNote), images, text, and frames — everything in the feature list above, working together out of the box.
A node-based tool. Define your own node types with typed input/output
ports and a pure compute function; the reactive DataFlowEngine propagates
values through the wired graph. This is the foundation for visual
programming, pipelines, and dashboards. See
docs/data-flow.md.
A presentation. Frames double as slides: order them explicitly or let
reading order decide, then step through with animated transitions. One method
call — engine.enterPresentation().
A collaborative canvas. The engine exposes remote-op methods
(addRemoteNode, applyRemoteNodeUpdate, deleteRemoteNode) and broadcasts
gesture awareness (live stroke, shape, drag, eraser, and laser previews) so
you can wire it to any transport or CRDT. Storage is deliberately not
SpatialBoard's business — serialize with toSBD()/toJSON() and persist
however you like.
An AI-native surface. getAgentState() returns a structured, budgeted
snapshot for LLM context (getAgentStateMarkdown() for prompts); a
programmatic creation API (createShape, createSticky, createEdge, …)
plus beginAgentAction() batching lets agents draw; and the whole board
round-trips through SBD — a markdown-compatible format that models can
both read and write. See docs/agents.md and
sbd-spec.md.
Custom nodes in one glance
Everything on the canvas is a node type — the built-ins use the same API you do. A custom type is a React component plus a definition object:
import type { NodeTypeDefinition, NodeRendererProps } from "spatialboard";
type CounterData = { count: number };
function Counter({ data, updateData }: NodeRendererProps<CounterData>) {
return (
<button onClick={() => updateData({ count: data.count + 1 })}>
Clicked {data.count} times
</button>
);
}
export const counterNodeType: NodeTypeDefinition<CounterData> = {
type: "counter",
component: Counter,
};
// <SpatialBoard nodeTypes={[...coreBoardNodes, counterNodeType]} />
Definitions can also declare container behavior, custom hit-testing, a
properties panel, lifecycle hooks (onCreate, onResize, onFlip, …), and
data-flow ports with a compute function. The full tour:
docs/custom-nodes.md.
The SBD format
Boards serialize to SBD — a markdown document with HTML-comment directives. It is diff-stable, hand-editable, and LLM-friendly:
<!--@meta sbd="3" background="dot-grid" -->
<!--@frame id="f1" x="100" y="100" w="400" h="300" label="Plan" -->
<!--@sticky id="s1" x="40" y="60" w="200" h="150" parent="f1" color="#FEF3C7" -->
Battery check at 06:00.
<!--@edge id="e1" from="s1" to="f1" style="dashed" -->
Round-trip with await engine.toSBD() / await engine.fromSBD(text). The
lower-level functions are serializeToSBD(nodes) and parseSBD(text) (which
returns { nodes, meta, warnings }). Spec in sbd-spec.md.
Documentation
| Guide | What's inside |
|---|---|
| ARCHITECTURE.md | The code-flow map: from App() to the engine to the render layers, with exact code references |
| docs/getting-started.md | Install, first board, engine lifecycle, persistence, key props |
| docs/design-philosophy.md | Why the engine lives outside React, the performance model, the layering |
| docs/custom-nodes.md | The NodeTypeDefinition API end to end, with a worked example |
| docs/data-flow.md | Ports, compute, the reactive DataFlowEngine, cycles, edge overlays |
| docs/agents.md | LLM/agent integration: state snapshots, programmatic drawing, SBD loops |
| docs/examples.md | Guided path through examples/, simplest to most complex |
| sbd-spec.md | The normative SBD format specification |
Examples
Four runnable apps — three focused tiers plus the kitchen sink, all hosted live at spatialboard.hishamkhalifa.com. All are self-contained, persist to localStorage, and work offline (PWA):
examples/basic(live) — the slim core board (<SpatialBoard />), no rich text and zero@blocknotepeers.examples/rich-text(live) — opt-in BlockNote nodes viaspatialboard/blocknote.examples/custom-nodes(live) — three custom node types wired as a live data-flow graph (Number × Number → Multiply → Gauge).examples/dev-app(live) — the development playground: every feature and ~40 custom node types. Every board pictured above ships here — Whiteboard, Half adder, Deck, and Mission Control all load from the debug panel.
From the package root:
npm install
npm run dev # examples/basic
npm run dev:rich-text # examples/rich-text
npm run dev:custom # examples/custom-nodes
npm run dev:app # examples/dev-app
docs/examples.md walks them from the one-liner up to the full playground.
Optional integrations
- GIF picker — pass
gifApiBaseUrland SpatialBoard renders a GIF search UI against your endpoint (Klipy-compatible response shape; seesrc/utils/klipy.ts). - Theming — override sidebar/panel tokens via the
themeprop; RTL and localization viadirectionandlocalization. - Read-only + preview modes —
readOnlykeeps pan/zoom/select alive while guarding all mutations;previewrenders a static board.
Roadmap
Roughly in the order I plan to tackle things — open an issue if you think the order is wrong:
- Better test coverage. The suite covers the engine core, serialization, export, and geometry, but the interaction layer (pointer gestures, selection, undo), the data-flow edge cases, and the React rendering side are still mostly verified by me clicking around.
- MCP server. SBD and
getAgentStateMarkdown()already give models a way to read and write boards; an MCP server would make that a standard plug instead of a bring-your-own-glue exercise. - CRDT collaboration example. The engine is transport-agnostic by design, but "wire it to any CRDT" deserves a worked end-to-end example.
Development
npm install
npm run dev # examples/basic (or dev:rich-text / dev:custom / dev:app)
npm run typecheck
npm run test
npm run build # dist/ (generated; not committed)
Contributing
Contributions are welcome — see CONTRIBUTING.md. Security reports go through SECURITY.md.
License
SpatialBoard is MIT licensed — development and production, no license keys, no watermarks. Third-party components and their licenses are inventoried in THIRD_PARTY_NOTICES.md; bundled and runtime fonts in FONTS.md.