opencanvas
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 6 GitHub stars
Code Pass
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
An infinite canvas your local LLM draws on. Typed widgets, BYO model (Claude/OpenAI/Ollama), MCP-native, local-first, MIT.
OpenCanvas
Stop reading AI replies. Watch them assemble on an infinite canvas.
Your LLM doesn't print markdown. It places typed widgets on a tldraw canvas: charts, diagrams, code, kanbans, tables, custom HTML. It learns your patterns and registers reusable widget templates as it goes. Any model with function calling. Bring your own key.
BYO model · MCP-native · Self-improving · Local-first · MIT

▶︎ Try the live demo · ⭐ Star on GitHub · 📡 API reference · 🏗 Architecture
Why OpenCanvas
Chat apps give you a scrolling wall of text. OpenCanvas gives the model a canvas and a toolbox.
Ask for a sales breakdown and you get a Vega-Lite chart next to a kanban of follow-ups next to the SQL that produced it. Drag them around. Pin the ones you keep. Every widget is a typed contract, so the model can't hand you broken output, and every conversation lands in a searchable local knowledge base.
| OpenCanvas | Claude Artifacts | Cursor Composer | v0 (Vercel) | ChatGPT Canvas | |
|---|---|---|---|---|---|
| Spatial canvas | ✅ tldraw infinite canvas | ❌ single pane | ❌ single pane | ❌ single pane | ⚠️ document only |
| Many typed widgets per turn | ✅ chart · kanban · code · mermaid · HTML · … | ⚠️ one at a time | ❌ | ⚠️ one at a time | ❌ |
| Bring your own model | ✅ Anthropic / OpenAI / Google / Groq / Ollama / OpenRouter | ❌ Claude only | ⚠️ a few | ❌ hosted | ❌ OpenAI only |
| MCP-native | ✅ first-class | ✅ via Claude.ai | ⚠️ recent | ❌ | ❌ |
| Self-improving widgets | ✅ extracts templates from its own renders | ❌ | ❌ | ❌ | ❌ |
| Open source, self-hosted | ✅ MIT | ❌ | ❌ | ❌ | ❌ |
| Fully local option | ✅ Ollama + bundled ONNX embeddings | ❌ | ⚠️ partial | ❌ | ❌ |
Under the hood, that translates to:
| Most chat apps | OpenCanvas | |
|---|---|---|
| Output | A stream of text | Typed widgets you click, drag, pin, link, export |
| Memory | One context window | SQLite + sqlite-vec KB; every chat searchable across conversations |
| Tools | A handful, baked in | 15 widget kinds, 12 agent tools, your MCP servers, runtime plugins |
| Drive it | Chat box only | REST API: any local process can render widgets |
| Privacy | Cloud round-trips | Single user, BYO credentials, runs on your machine |
Install
git clone https://github.com/ashark-ai-05/opencanvas.git
cd opencanvas
pnpm install
cp .env.example .env # at least one provider key
pnpm electron:dev # backend + Vite + Electron, one command
Headless without Electron: pnpm dev, then open http://127.0.0.1:3458.
Packaged installers (.dmg / .exe / .AppImage) ship on the Releases tab. Want a public demo of your own? docs/deploy-railway.md gets you there in about ten minutes.
Pick any LLM
Every provider gets the same agent: same tools, same widget surface, same UX. The model is a config switch.
| Provider | Auth | Notes |
|---|---|---|
| Anthropic | ANTHROPIC_API_KEY |
Claude Sonnet / Opus 4+; extended thinking auto-on |
| OpenAI | OPENAI_API_KEY |
GPT-4o, GPT-4.1, o-series |
| Google Gemini | GOOGLE_API_KEY |
Gemini 2.5 / 3.x Flash and Pro; thinking shows in chat |
| Groq | GROQ_API_KEY |
Llama / Mixtral / Kimi at Groq speeds |
| OpenRouter | OPENROUTER_API_KEY |
One key, hundreds of models |
| Ollama | none, local | Any model you've pulled (llama3.2, qwen2.5-coder, …) |
| Sourcegraph Amp | AMP_API_KEY |
Hosted agent loop (legacy path) |
Tool calling runs on the Vercel AI SDK, so any provider with function-calling support behaves the same. Embedders are pluggable too: bundled ONNX (offline), OpenAI, Voyage, or Ollama. Architecture notes live in docs/plans/unified-agent.md.
// ~/.opencanvas/config.json
{
"activeProfile": "local",
"profiles": [{
"name": "local",
"llm": { "provider": "ollama", "model": "llama3.1:8b" },
"embed": { "provider": "ollama", "model": "nomic-embed-text" },
"sources": []
}]
}
The widget catalog
15 kinds. Markdown · code · tables · timelines · file trees · kanban · tasks · sticky notes · composite cards · charts (Vega-Lite) · calendars · clock / timer / stopwatch / pomodoro · web embeds (sandboxed iframes) · plugin iframes · generic fallback.
Every widget is a typed contract: a Zod schema for props plus a React component. Add a new kind without retraining anything.
Runtime plugins. The agent can call register_widget_kind mid-conversation to declare a brand-new widget on the fly. A Python REPL (Pyodide) and a JS REPL ship as examples. Plugin iframes run with sandbox="allow-scripts" and no allow-same-origin: null origin, no access to the parent DOM.
MCP-native
Add any MCP server under profiles[].sources. Works with every provider.
{
"sources": [{
"id": "dev-fs",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/Development"]
}]
}
Verify with pnpm cli --probe-sources. The agent calls them as mcp__dev-fs__<tool>.
Drive it from any process
Cron jobs, file watchers, shell scripts: anything on your machine can render widgets. The browser holds an SSE connection to /v1/canvas/events; external POSTs push directives onto the per-conversation event bus.
# Place a Vega-Lite chart from your terminal
curl -X POST http://127.0.0.1:3457/v1/canvas/widgets \
-H 'content-type: application/json' \
-H "x-opencanvas-token: $(cat ~/.opencanvas/auth-token)" \
-d '{"kind":"chart","role":"primary","payload":{"title":"Sales","spec":{...}}}'
REST reference. Browse the live API docs or read the OpenAPI 3.1 spec. A running backend serves the same UI at http://127.0.0.1:3457/docs.
Auth. A 256-bit token is generated on first run and stored at ~/.opencanvas/auth-token (mode 0600). Required on every state-mutating route. Details in SECURITY.md.
Self-improving knowledge base
Every conversation auto-indexes into the same SQLite + sqlite-vec store as your docs and code. Old turns become searchable through search_kb. Retrieval is hybrid BM25 + vector. Re-indexing is idempotent: unchanged content costs zero LLM calls.
pnpm cli --index ./docs # markdown / text → chunks + embeddings
pnpm cli --index-code ./src # tree-sitter chunks + symbols
pnpm cli --search "<query>" # hybrid search
The agent also learns you. Widget kinds you keep and pin score higher next time; kinds you dismiss get placed less. Per conversation, stored locally, no remote tracking.
Architecture
┌────────────────────┐ ┌─────────────────────────┐
│ Vite + React + │ /v1/chat │ Hono backend │
│ tldraw │ ─────────────→ │ │
│ • floating chat │ │ provider adapters │
│ • canvas │ │ 12 in-process tools │
│ • palette ⌘K │ ←── SSE ────── │ + external MCP servers │
│ • history │ /v1/canvas/ │ + plugin registry │
└────────────────────┘ events │ │ │
│ ▼ │
│ SQLite + sqlite-vec │
└─────────────────────────┘
tldraw v3 with one shape util per widget kind. Zustand for app state. AI SDK for chat streaming. Hono + better-sqlite3 + sqlite-vec on the backend. Electron wrapper for the desktop build.
Development
pnpm test # backend (vitest, Node)
pnpm vitest run --config app/vite.config.ts # frontend (vitest + jsdom)
pnpm typecheck # tsc --noEmit
pnpm app:build # production bundle
pnpm dist # electron-builder installer
610 tests: 396 backend, 214 frontend. CI runs both suites plus a production dependency audit on every push.
Status
Experimental. Solo project, MIT. No telemetry, no remote services unless you point it at one.
Issues and PRs welcome. See CONTRIBUTING.md. Security reports go through SECURITY.md.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found