{"skill":{"slug":"canvas","displayName":"Canvas","summary":"Display and control HTML content on connected Mac, iOS, or Android nodes via a web-based canvas with live reload and remote actions.","description":"# Canvas Skill\n\nDisplay HTML content on connected OpenClaw nodes (Mac app, iOS, Android).\n\n## Overview\n\nThe canvas tool lets you present web content on any connected node's canvas view. Great for:\n\n- Displaying games, visualizations, dashboards\n- Showing generated HTML content\n- Interactive demos\n\n## How It Works\n\n### Architecture\n\n```\n┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐\n│  Canvas Host    │────▶│   Node Bridge    │────▶│  Node App   │\n│  (HTTP Server)  │     │  (TCP Server)    │     │ (Mac/iOS/   │\n│  Port 18793     │     │  Port 18790      │     │  Android)   │\n└─────────────────┘     └──────────────────┘     └─────────────┘\n```\n\n1. **Canvas Host Server**: Serves static HTML/CSS/JS files from `canvasHost.root` directory\n2. **Node Bridge**: Communicates canvas URLs to connected nodes\n3. **Node Apps**: Render the content in a WebView\n\n### Tailscale Integration\n\nThe canvas host server binds based on `gateway.bind` setting:\n\n| Bind Mode  | Server Binds To     | Canvas URL Uses            |\n| ---------- | ------------------- | -------------------------- |\n| `loopback` | 127.0.0.1           | localhost (local only)     |\n| `lan`      | LAN interface       | LAN IP address             |\n| `tailnet`  | Tailscale interface | Tailscale hostname         |\n| `auto`     | Best available      | Tailscale > LAN > loopback |\n\n**Key insight:** The `canvasHostHostForBridge` is derived from `bridgeHost`. When bound to Tailscale, nodes receive URLs like:\n\n```\nhttp://<tailscale-hostname>:18793/__openclaw__/canvas/<file>.html\n```\n\nThis is why localhost URLs don't work - the node receives the Tailscale hostname from the bridge!\n\n## Actions\n\n| Action     | Description                          |\n| ---------- | ------------------------------------ |\n| `present`  | Show canvas with optional target URL |\n| `hide`     | Hide the canvas                      |\n| `navigate` | Navigate to a new URL                |\n| `eval`     | Execute JavaScript in the canvas     |\n| `snapshot` | Capture screenshot of canvas         |\n\n## Configuration\n\nIn `~/.openclaw/openclaw.json`:\n\n```json\n{\n  \"canvasHost\": {\n    \"enabled\": true,\n    \"port\": 18793,\n    \"root\": \"/Users/you/clawd/canvas\",\n    \"liveReload\": true\n  },\n  \"gateway\": {\n    \"bind\": \"auto\"\n  }\n}\n```\n\n### Live Reload\n\nWhen `liveReload: true` (default), the canvas host:\n\n- Watches the root directory for changes (via chokidar)\n- Injects a WebSocket client into HTML files\n- Automatically reloads connected canvases when files change\n\nGreat for development!\n\n## Workflow\n\n### 1. Create HTML content\n\nPlace files in the canvas root directory (default `~/clawd/canvas/`):\n\n```bash\ncat > ~/clawd/canvas/my-game.html << 'HTML'\n<!DOCTYPE html>\n<html>\n<head><title>My Game</title></head>\n<body>\n  <h1>Hello Canvas!</h1>\n</body>\n</html>\nHTML\n```\n\n### 2. Find your canvas host URL\n\nCheck how your gateway is bound:\n\n```bash\ncat ~/.openclaw/openclaw.json | jq '.gateway.bind'\n```\n\nThen construct the URL:\n\n- **loopback**: `http://127.0.0.1:18793/__openclaw__/canvas/<file>.html`\n- **lan/tailnet/auto**: `http://<hostname>:18793/__openclaw__/canvas/<file>.html`\n\nFind your Tailscale hostname:\n\n```bash\ntailscale status --json | jq -r '.Self.DNSName' | sed 's/\\.$//'\n```\n\n### 3. Find connected nodes\n\n```bash\nopenclaw nodes list\n```\n\nLook for Mac/iOS/Android nodes with canvas capability.\n\n### 4. Present content\n\n```\ncanvas action:present node:<node-id> target:<full-url>\n```\n\n**Example:**\n\n```\ncanvas action:present node:mac-63599bc4-b54d-4392-9048-b97abd58343a target:http://peters-mac-studio-1.sheep-coho.ts.net:18793/__openclaw__/canvas/snake.html\n```\n\n### 5. Navigate, snapshot, or hide\n\n```\ncanvas action:navigate node:<node-id> url:<new-url>\ncanvas action:snapshot node:<node-id>\ncanvas action:hide node:<node-id>\n```\n\n## Debugging\n\n### White screen / content not loading\n\n**Cause:** URL mismatch between server bind and node expectation.\n\n**Debug steps:**\n\n1. Check server bind: `cat ~/.openclaw/openclaw.json | jq '.gateway.bind'`\n2. Check what port canvas is on: `lsof -i :18793`\n3. Test URL directly: `curl http://<hostname>:18793/__openclaw__/canvas/<file>.html`\n\n**Solution:** Use the full hostname matching your bind mode, not localhost.\n\n### \"node required\" error\n\nAlways specify `node:<node-id>` parameter.\n\n### \"node not connected\" error\n\nNode is offline. Use `openclaw nodes list` to find online nodes.\n\n### Content not updating\n\nIf live reload isn't working:\n\n1. Check `liveReload: true` in config\n2. Ensure file is in the canvas root directory\n3. Check for watcher errors in logs\n\n## URL Path Structure\n\nThe canvas host serves from `/__openclaw__/canvas/` prefix:\n\n```\nhttp://<host>:18793/__openclaw__/canvas/index.html  → ~/clawd/canvas/index.html\nhttp://<host>:18793/__openclaw__/canvas/games/snake.html → ~/clawd/canvas/games/snake.html\n```\n\nThe `/__openclaw__/canvas/` prefix is defined by `CANVAS_HOST_PATH` constant.\n\n## Tips\n\n- Keep HTML self-contained (inline CSS/JS) for best results\n- Use the default index.html as a test page (has bridge diagnostics)\n- The canvas persists until you `hide` it or navigate away\n- Live reload makes development fast - just save and it updates!\n- A2UI JSON push is WIP - use HTML files for now\n","tags":{"latest":"1.0.0"},"stats":{"comments":0,"downloads":5993,"installsAllTime":751,"installsCurrent":749,"stars":2,"versions":1},"createdAt":1771261950369,"updatedAt":1778991000535},"latestVersion":{"version":"1.0.0","createdAt":1771261950369,"changelog":"Initial release of the Canvas skill for OpenClaw:\n\n- Adds ability to display HTML content on Mac, iOS, and Android node canvases\n- Supports presenting, navigating, hiding, JavaScript eval, and screenshot actions for the canvas\n- Integrates Tailscale and LAN binding options for easy remote access\n- Includes live reload for instant updates during development\n- Provides detailed setup, configuration, and troubleshooting instructions\n- Serves files from a configurable root directory via a dedicated HTTP server","license":null},"metadata":null,"owner":{"handle":"lura2","userId":"s1712gn735xy90kjt6xzq85cm1885gcj","displayName":"lura2","image":"https://avatars.githubusercontent.com/u/30603095?v=4"},"moderation":null}