canvas
GitHub在连接的 Brigde 节点(Mac/iOS/Android)上展示 HTML 内容,如游戏、可视化图表和交互演示。通过本地或 Tailscale 网络提供静态资源,支持实时重载、页面导航及 JS 执行。
Trigger Scenarios
Install
npx skills add spinabot/brigade --skill canvas -g -y
SKILL.md
Frontmatter
{
"name": "canvas",
"description": "Display HTML content — games, visualizations, dashboards, generated HTML, interactive demos — on a connected canvas-capable display surface. Use when you need to present visual or interactive web content on a screen."
}
Canvas Skill
Display HTML content on connected Brigade nodes (Mac app, iOS, Android).
Overview
The canvas tool lets you present web content on any connected node's canvas view. Great for:
- Displaying games, visualizations, dashboards
- Showing generated HTML content
- Interactive demos
How It Works
Architecture
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ Canvas Host │────▶│ Node Bridge │────▶│ Node App │
│ (HTTP Server) │ │ (TCP Server) │ │ (Mac/iOS/ │
│ Port 18793 │ │ Port 18790 │ │ Android) │
└─────────────────┘ └──────────────────┘ └─────────────┘
- Canvas Host Server: Serves static HTML/CSS/JS files from
canvasHost.rootdirectory - Node Bridge: Communicates canvas URLs to connected nodes
- Node Apps: Render the content in a WebView
Tailscale Integration
The canvas host server binds based on gateway.bind setting:
| Bind Mode | Server Binds To | Canvas URL Uses |
|---|---|---|
loopback |
127.0.0.1 | localhost (local only) |
lan |
LAN interface | LAN IP address |
tailnet |
Tailscale interface | Tailscale hostname |
auto |
Best available | Tailscale > LAN > loopback |
Key insight: The canvasHostHostForBridge is derived from bridgeHost. When bound to Tailscale, nodes receive URLs like:
http://<tailscale-hostname>:18793/__brigade__/canvas/<file>.html
This is why localhost URLs don't work - the node receives the Tailscale hostname from the bridge!
Actions
| Action | Description |
|---|---|
present |
Show canvas with optional target URL |
hide |
Hide the canvas |
navigate |
Navigate to a new URL |
eval |
Execute JavaScript in the canvas |
snapshot |
Capture screenshot of canvas |
Configuration
In the active Brigade config file ($BRIGADE_CONFIG_PATH, default ~/.brigade/brigade.json):
{
"canvasHost": {
"enabled": true,
"port": 18793,
"root": "/Users/you/brigade/canvas",
"liveReload": true
},
"gateway": {
"bind": "auto"
}
}
Live Reload
When liveReload: true (default), the canvas host:
- Watches the root directory for changes (via chokidar)
- Injects a WebSocket client into HTML files
- Automatically reloads connected canvases when files change
Great for development!
Workflow
1. Create HTML content
Place files in the canvas root directory (default ~/brigade/canvas/):
cat > ~/brigade/canvas/my-game.html << 'HTML'
<!DOCTYPE html>
<html>
<head><title>My Game</title></head>
<body>
<h1>Hello Canvas!</h1>
</body>
</html>
HTML
2. Find your canvas host URL
Check how your gateway is bound:
CONFIG_PATH="${BRIGADE_CONFIG_PATH:-${BRIGADE_STATE_DIR:-$HOME/.brigade}/brigade.json}"
cat "$CONFIG_PATH" | jq '.gateway.bind'
Then construct the URL:
- loopback:
http://127.0.0.1:18793/__brigade__/canvas/<file>.html - lan/tailnet/auto:
http://<hostname>:18793/__brigade__/canvas/<file>.html
Find your Tailscale hostname:
tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//'
3. Find connected nodes
brigade nodes list
Look for Mac/iOS/Android nodes with canvas capability.
4. Present content
canvas action:present node:<node-id> target:<full-url>
Example:
canvas action:present node:mac-63599bc4-b54d-4392-9048-b97abd58343a target:http://peters-mac-studio-1.sheep-coho.ts.net:18793/__brigade__/canvas/snake.html
5. Navigate, snapshot, or hide
canvas action:navigate node:<node-id> url:<new-url>
canvas action:snapshot node:<node-id>
canvas action:hide node:<node-id>
Debugging
White screen / content not loading
Cause: URL mismatch between server bind and node expectation.
Debug steps:
- Check server bind:
CONFIG_PATH="${BRIGADE_CONFIG_PATH:-${BRIGADE_STATE_DIR:-$HOME/.brigade}/brigade.json}"; cat "$CONFIG_PATH" | jq '.gateway.bind' - Check what port canvas is on:
lsof -i :18793 - Test URL directly:
curl http://<hostname>:18793/__brigade__/canvas/<file>.html
Solution: Use the full hostname matching your bind mode, not localhost.
"node required" error
Always specify node:<node-id> parameter.
"node not connected" error
Node is offline. Use brigade nodes list to find online nodes.
Content not updating
If live reload isn't working:
- Check
liveReload: truein config - Ensure file is in the canvas root directory
- Check for watcher errors in logs
URL Path Structure
The canvas host serves from /__brigade__/canvas/ prefix:
http://<host>:18793/__brigade__/canvas/index.html → ~/brigade/canvas/index.html
http://<host>:18793/__brigade__/canvas/games/snake.html → ~/brigade/canvas/games/snake.html
The /__brigade__/canvas/ prefix is defined by CANVAS_HOST_PATH constant.
Tips
- Keep HTML self-contained (inline CSS/JS) for best results
- Use the default index.html as a test page (has bridge diagnostics)
- The canvas persists until you
hideit or navigate away - Live reload makes development fast - just save and it updates!
- A2UI JSON push is WIP - use HTML files for now
Version History
- db99206 Current 2026-07-05 10:58


