🖥️ Canvas-OS
Canvas as an app platform. Build, store, and run rich visual apps on the OpenClaw Canvas.
Canvas as an app platform. Build, store, and run rich visual apps on the OpenClaw Canvas.
Real data. Real impact.
Emerging
Developers
Per week
Open source
Skills give you superpowers. Install in 30 seconds.
Canvas as an app platform. Build, store, and run rich visual apps on the OpenClaw Canvas.
You are an OS. Canvas is the window. Apps are built locally and run on Canvas.
Rich HTML/CSS/JS UIs — not just text. Full interactivity, animations, live data.
| Command | What Jarvis Does |
|---|---|
| "Open [app]" | Start server, navigate Canvas, inject data |
| "Build me a [type]" | Create app from template, open it |
| "Update [element]" | Inject JS to modify live |
| "Show [data] on canvas" | Quick A2UI display |
| "Close canvas" | Stop server, hide Canvas |
Key principle: Apps run on Canvas, not in a browser tab. Canvas is your UI window.
Canvas has security restrictions that block file path access. Three methods work:
| Method | When to Use | Pros | Cons |
|---|---|---|---|
| Localhost Server | Complex apps, external assets | Full browser features | Requires port management |
| Direct HTML Injection | Quick displays, demos | Instant, no server needed | No external assets, size limit |
| Data URLs | Small content | Self-contained | Unreliable on some systems |
❌ Does NOT work:
file:///path/to/file.html (blocked by Canvas security)
📖 See:
CANVAS-LOADING.md for detailed guide + troubleshooting
Helper script:
canvas-inject.py — Formats HTML for direct injection
~/.openclaw/workspace/apps/[app-name]/ ├── index.html # The UI (self-contained recommended) ├── data.json # Persistent state └── manifest.json # App metadata
cd ~/.openclaw/workspace/apps/[app-name] python3 -m http.server [PORT] > /dev/null 2>&1 &
NODE="Your Node Name" # Get from: openclaw nodes status openclaw nodes canvas navigate --node "$NODE" "http://localhost:[PORT]/"
Important: This opens the app on Canvas (the visual panel), NOT in a browser.
openclaw nodes canvas eval --node "$NODE" --js "app.setData({...})"
Note: The
openclaw-canvas:// URL scheme has issues in current OpenClaw versions. Use http://localhost: instead.
What this does: Displays the app on Canvas (the visual panel), not in a browser tab.
Full sequence:
NODE="Your Node Name" PORT=9876 APP="my-app"1. Kill any existing server on the port
lsof -ti:$PORT | xargs kill -9 2>/dev/null
2. Start server
cd ~/.openclaw/workspace/apps/$APP python3 -m http.server $PORT > /dev/null 2>&1 &
3. Wait for server
sleep 1
4. Navigate Canvas
openclaw nodes canvas navigate --node "$NODE" "http://localhost:$PORT/"
5. Inject data
openclaw nodes canvas eval --node "$NODE" --js "app.loadData({...})"
When to use: File paths don't work in Canvas (security sandboxing). Data URLs can be unreliable. Use this for instant displays without localhost.
# Example using canvas tool canvas.present(url="about:blank", target=node_name)html_content = """<!DOCTYPE html> <html> <head> <style> body { background: #667eea; color: white; padding: 40px; } .card { background: white; color: #333; padding: 30px; border-radius: 16px; } </style> </head> <body> <div class="card"> <h1>Your Content Here</h1> </div> </body> </html>"""
Escape backticks and inject
js_code = f"""document.open(); document.write(
); document.close();"""{html_content}canvas.eval(javaScript=js_code, target=node_name)
Key limitation: File paths (
file:///path/to/file.html) are blocked in Canvas for security. Always use localhost or direct injection.
Every app should expose a
window.app or window.[appname] object:
window.app = { // Update values setValue: (key, val) => { document.getElementById(key).textContent = val; },// Bulk update loadData: (data) => { /* render all */ },
// Notifications notify: (msg) => { /* show toast */ } };
Apps send commands back via deep links:
function sendToAgent(message) { window.location.href = `openclaw://agent?message=${encodeURIComponent(message)}`; }// Button click → agent command document.getElementById('btn').onclick = () => { sendToAgent('Refresh my dashboard'); };
Stats cards, progress bars, lists. Self-contained HTML.
dashboard.setRevenue(), dashboard.setProgress(), dashboard.notify()Habits/tasks with checkboxes and streaks. Self-contained HTML.
tracker.setItems(), tracker.addItem(), tracker.toggleItem()For temporary displays without a full app:
openclaw nodes canvas a2ui push --node "$NODE" --text " 📊 QUICK STATUSRevenue: $500 Users: 100
Done! "
| App Type | Default Port |
|---|---|
| Dashboard | 9876 |
| Tracker | 9877 |
| Timer | 9878 |
| Display | 9879 |
| Custom | 9880+ |
:root { --bg-primary: #0a0a0a; --bg-card: #1a1a2e; --accent-green: #00d4aa; --accent-blue: #4a9eff; --accent-orange: #f59e0b; --text-primary: #fff; --text-muted: #888; --border: #333; }
window.app.*App opens in browser instead of Canvas?
openclaw nodes canvas navigate, not just open"Not Found" on Canvas?
file:/// URLs for security (sandboxing)canvas eval + document.write() insteadcurl http://localhost:[PORT]/http://localhost: not openclaw-canvas:// (URL scheme has issues)Canvas shows "Not Found" even with correct URL?
App not updating?
openclaw nodes canvas eval --js "typeof window.app"Server port already in use?
lsof -ti:[PORT] | xargs kill -9Python helper for direct HTML injection (Method 2).
# Example usage in Python from canvas_inject import inject_html_to_canvashtml = open("my-dashboard.html").read() commands = inject_html_to_canvas(html, node_name="Your Node")
Then use canvas tool with these commands
canvas.present(**commands["step1_present"]) canvas.eval(**commands["step2_inject"])
Or just follow the pattern manually (see Method 2 in "Opening an App").
No automatic installation available. Please visit the source repository for installation instructions.
View Installation Instructions1,500+ AI skills, agents & workflows. Install in 30 seconds. Part of the Torly.ai family.
© 2026 Torly.ai. All rights reserved.