Skip to content
Conduit

mcp server

spatialboard

A React + TypeScript spatial canvas and node-board library for whiteboards, visual editors, and graph interfaces.

hishamk/spatialboardcommunitylistedunreviewed

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, 2026

SpatialBoard 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 compute function; the reactive DataFlowEngine propagates 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/engine imports without React or CSS, so the engine runs in tests, scripts, and agent tooling.
  • Interop — imports Excalidraw .excalidrawlib shape 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:

EntryWhat you get
spatialboardThe board component, the engine, and the built-in node types
spatialboard/blocknoteOpt-in rich-text nodes (adds the BlockNote ^0.46 + Mantine ^8 peers)
spatialboard/engineThe 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

GuideWhat's inside
ARCHITECTURE.mdThe code-flow map: from App() to the engine to the render layers, with exact code references
docs/getting-started.mdInstall, first board, engine lifecycle, persistence, key props
docs/design-philosophy.mdWhy the engine lives outside React, the performance model, the layering
docs/custom-nodes.mdThe NodeTypeDefinition API end to end, with a worked example
docs/data-flow.mdPorts, compute, the reactive DataFlowEngine, cycles, edge overlays
docs/agents.mdLLM/agent integration: state snapshots, programmatic drawing, SBD loops
docs/examples.mdGuided path through examples/, simplest to most complex
sbd-spec.mdThe 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 @blocknote peers.
  • examples/rich-text (live) — opt-in BlockNote nodes via spatialboard/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 gifApiBaseUrl and SpatialBoard renders a GIF search UI against your endpoint (Klipy-compatible response shape; see src/utils/klipy.ts).
  • Theming — override sidebar/panel tokens via the theme prop; RTL and localization via direction and localization.
  • Read-only + preview modes — readOnly keeps pan/zoom/select alive while guarding all mutations; preview renders a static board.

Roadmap

Roughly in the order I plan to tackle things — open an issue if you think the order is wrong:

  1. 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.
  2. 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.
  3. 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.

↑↓ navigate openesc close