A hand-drawn sketch pad for Claude Code sessions.
Draw in your browser. Pick a Claude Code session. Submit. Claude sees your sketch as a PNG alongside your prompt.
Press ✏️ → Send ✉️ → Claude reads the image.
Tip
Use a tablet with a stylus for natural sketching, or a mouse for quick wireframes. Each Claude session gets its own isolated canvas that persists across page reloads.
|
Sketch a UI and ask Claude to implement it. Pin a screenshot — drop numbered annotations, submit, get per-pin replies. Draw a diagram and ask Claude to build the architecture. Iterate on a render — Claude can push images and annotations back to your canvas. |
Text prompts struggle with spatial intent. A quick sketch communicates layout, hierarchy, and motion in a single frame — things that would take paragraphs to describe. Structured annotations close the loop in the other direction: Claude resolves your pins by ID, replies inline, and pushes its own annotations on rendered output for a real review cycle. |
📑 Table of contents
Runs on Linux, macOS, and Windows — Bun is the only prerequisite.
# 1. Install
git clone https://github.com/SDS-Mode/trayce.git
cd trayce
bun install
bun run build:client
# 2. Start the server
bun run start
# → [trayce] Open: http://localhost:9740?token=abc123…Open the printed URL in your browser. Then connect a Claude Code session:
# From any project directory
bun run /path/to/trayce/scripts/setup.ts --label my-project
claude --dangerously-load-development-channels server:trayceDraw something · select the session from the dropdown · click Submit (or press Ctrl + Enter).
|
Pen · Pencil · Marker · Watercolor · Highlighter.
Full support for pressure, size |
Up to 20 layers with blend modes (multiply, screen, overlay, …), per-layer opacity & visibility. Paste, drop, or pick images as transform layers. |
Each Claude session gets its own canvas, persisted to IndexedDB. Tab locking prevents collisions between browser tabs. |
|
Place pins, text, or callouts on the canvas as a parallel layer above your sketch. Each one is a typed, addressable object with a status lifecycle ( |
Connect multiple Claude Code sessions simultaneously. Pick the target from a dropdown, draw once, submit. Claude can also push images back via |
Four side-panel tabs: Response, Transcript (tool calls), Usage (tokens + cost), Annotations (status, replies, delete + inline edit). Theme accent threads through pins/callouts so they match the rest of the UI. |
| Action | Key | Action | Key | |
|---|---|---|---|---|
| 🖊️ Pen | B |
✋ Pan | Space + drag · or middle-mouse drag |
|
| ✏️ Pencil | N |
🔍 Zoom | Scroll wheel | |
| 🖍️ Marker | M |
[ / ] |
||
| 🎨 Watercolor | W |
↶ Undo / ↷ Redo | Ctrl + Z / Ctrl + Y |
|
| 🖌️ Highlighter | H |
📤 Submit | Ctrl + Enter |
|
| 📍 Annotate mode | A |
⎋ Exit annotate | Esc |
While Annotate mode is active: T text · P pin · C callout. Brush shortcuts are suppressed so accidental keystrokes don't draw.
bun run /path/to/trayce/scripts/setup.ts --label my-projectCross-platform — works on Linux, macOS, and Windows. Writes the MCP config into your project's .mcp.json. POSIX bash equivalents exist in scripts/ with .sh suffixes, but the .ts versions are preferred and are what the bundled Claude Code plugin invokes.
Then start Claude with the channel flag:
claude --dangerously-load-development-channels server:trayce| Flag | Effect |
|---|---|
--label NAME |
Session label in the trayce dropdown (default: directory name) |
--global |
Install to ~/.claude.json instead of project .mcp.json |
--uninstall |
Remove trayce from MCP config |
Add to .mcp.json in your project root (or ~/.claude.json for global):
{
"mcpServers": {
"trayce": {
"command": "bun",
"args": ["run", "/path/to/trayce/bridge/index.ts"],
"env": {
"TRAYCE_LABEL": "my-project"
}
}
}
}bun run start # Start in foreground
bun run scripts/start.ts # Start detached (reuses existing instance)
bun run scripts/stop.ts # Stop detached server
bun run scripts/status.ts # JSON status (running PID/URL or reason not running)
bun run dev # Development with hot reloadThe server writes state to $TMPDIR/trayce/state.json (PID, port, token):
| Platform | Resolves to |
|---|---|
| 🐧 Linux / 🍎 macOS | /tmp/trayce/state.json |
| 🪟 Windows | %TEMP%\trayce\state.json |
Bridges read this automatically — no manual token configuration needed. Restart failures and startup errors are captured in the sibling server.log.
Choose from presets via the resolution dropdown:
- 🖥️
1920 × 1080(1080p) - 🖥️
2560 × 1440(1440p) - 🖥️
3840 × 2160(4K) - 🟥
4096 × 4096(square large)
Exports at full document resolution regardless of zoom level.
Click the gear icon (top-right) to pick a theme preset, accent color, font, and two independent scale sliders — text size and control size (80% – 150%, discrete 10% steps). Bigger panel text doesn't require a bloated toolbar; bigger tap targets don't require giant labels. Settings persist in localStorage. Annotation pins/callouts also follow the selected accent color.
Copy .env.example → .env for local overrides; the file ships with every supported var commented out at its default value.
| Variable | Default | Description |
|---|---|---|
🌐 TRAYCE_HOST |
0.0.0.0 |
Bind address |
🔢 TRAYCE_PORT |
9740 |
Server port |
🔑 TRAYCE_TOKEN |
(auto) | Auth token (auto-generated if unset) |
🚪 TRAYCE_NO_AUTH |
false |
Disable token authentication |
📂 TRAYCE_SUBMISSIONS_DIR |
$TMPDIR/trayce/submissions |
PNG storage path |
📄 TRAYCE_STATE_FILE |
$TMPDIR/trayce/state.json |
Server state file |
🗂️ TRAYCE_CLIENT_DIR |
dist/client |
Where the client bundle lives (override for tests) |
🏷️ TRAYCE_LABEL |
(directory name) | Bridge-side: label shown in the session dropdown |
📦 TRAYCE_CONTAINER |
(auto) | 1 when running inside a container; auto-detected via /.dockerenv on Linux |
Token priority: TRAYCE_NO_AUTH=true > TRAYCE_TOKEN > auto-generate.
| 📚 Topic | Description |
|---|---|
| 🏗️ Architecture & data flow | Three-process model, submission flow, WebSocket protocol |
| 📦 Container deployment | Running trayce in a container with podman / docker-compose |
| 🔒 Security | Token auth, rate limiting, CSP headers, size limits |
bun run dev # Server with --watch hot reload
bun run dev:client # Client bundler in watch mode
bun test # Run all tests (bun's built-in test runner)
bun run typecheck # tsc --noEmit
bun run check # Biome lint + formatNote
Tests mirror source structure under tests/. Integration and e2e tests live alongside unit tests — including tests/e2e/client-boot.test.ts which runs on both Ubuntu and Windows CI runners.
trayce/
├── 🖥️ server/ Bun HTTP + WebSocket server
├── 🔌 bridge/ MCP Channel bridge (spawned per Claude session)
├── 🎨 client/ Browser canvas app (PixiJS + perfect-freehand)
│ ├── brushes/ Brush implementations (pen, pencil, marker, …)
│ └── tools/ Non-brush tools (image import, shapes, text, …)
├── 🔗 shared/ Protocol types, Zod schemas, cross-platform paths
├── 🧪 tests/ Bun test runner (mirrors source structure)
├── 📜 scripts/ Lifecycle scripts (.ts cross-platform + .sh POSIX)
└── 🧩 plugin/ Claude Code plugin with trayce skills
Just three runtime dependencies — Bun provides HTTP, WebSocket, and file I/O natively.
| 📦 Package | Purpose |
|---|---|
pixi.js |
WebGL / WebGPU rendering and layer compositing |
perfect-freehand |
Pressure-sensitive stroke outlines |
@modelcontextprotocol/sdk |
MCP Channel protocol for the bridge |
Plus zod for runtime validation at the WebSocket parse boundary.
MIT © 2026 SDS-Mode