/canvas-gen — Canvas Generation from Beads
TL;DR: Auto-generate Obsidian Canvas files from beads issues for visual task tracking.
Usage
code
/canvas-gen # Generate from all in-progress issues /canvas-gen <epic-id> # Generate from specific epic /canvas-gen --template=work # Use work session template /canvas-gen --output=<name> # Custom output filename
Canvas Types
| Template | Use Case | Structure |
|---|---|---|
| work | Active work session | Epic → Tasks → Subtasks |
| hierarchy | Skill/agent overview | Layers with relationships |
| timeline | Sprint/week view | Date-ordered columns |
Execution
code
1. PARSE input:
epic_id = PARSE argument (optional)
template = PARSE --template flag (default: "work")
output_name = PARSE --output flag (default: auto-generated)
2. FETCH beads data:
IF epic_id:
issues = Bash("bd show {epic_id} --format=json")
children = Bash("bd list --parent={epic_id} --format=json")
ELSE:
issues = Bash("bd list --status=in_progress --format=json")
3. GENERATE canvas structure:
canvas = {
nodes: [],
edges: []
}
# Layout constants
NODE_WIDTH = 250
NODE_HEIGHT = 60
COLUMN_GAP = 300
ROW_GAP = 100
# Position tracking
x_offset = 0
y_offset = 0
# Generate nodes by status
FOR EACH status IN ["in_progress", "blocked", "pending", "completed"]:
status_issues = issues.filter(i => i.status == status)
column_y = 0
FOR EACH issue IN status_issues:
node = {
id: issue.id,
type: "text",
x: x_offset,
y: column_y,
width: NODE_WIDTH,
height: NODE_HEIGHT,
text: FORMAT_NODE_TEXT(issue),
color: STATUS_COLOR(status)
}
canvas.nodes.push(node)
column_y += ROW_GAP
x_offset += COLUMN_GAP
4. GENERATE edges (dependencies):
FOR EACH issue IN issues:
IF issue.blocked_by:
FOR EACH blocker_id IN issue.blocked_by:
edge = {
id: GENERATE_ID(),
fromNode: blocker_id,
toNode: issue.id,
fromSide: "right",
toSide: "left"
}
canvas.edges.push(edge)
5. FORMAT_NODE_TEXT(issue):
RETURN "**{issue.id}**\n{issue.title}\n_{issue.status}_"
6. STATUS_COLOR(status):
MATCH status:
"in_progress" → "5" # Purple
"blocked" → "1" # Red
"pending" → "4" # Yellow
"completed" → "4" # Green (using yellow, no green in Obsidian)
DEFAULT → "0"
7. WRITE canvas file:
IF output_name:
filename = output_name + ".canvas"
ELSE:
filename = "beads-" + DATE + ".canvas"
filepath = "obsidian-vault/canvas/" + filename
Write(filepath, JSON.stringify(canvas, null, 2))
8. OUTPUT confirmation:
OUTPUT "✅ Canvas generated: {filepath}"
OUTPUT "Nodes: {canvas.nodes.length} | Edges: {canvas.edges.length}"
OUTPUT "Open in Obsidian to view"
Output Format
Canvas JSON Structure
json
{
"nodes": [
{
"id": "beads-abc",
"type": "text",
"x": 0,
"y": 0,
"width": 250,
"height": 60,
"text": "**beads-abc**\nImplement feature X\n_in_progress_",
"color": "5"
}
],
"edges": [
{
"id": "edge-1",
"fromNode": "beads-xyz",
"toNode": "beads-abc",
"fromSide": "right",
"toSide": "left"
}
]
}
Color Reference
Obsidian Canvas colors:
| Color Code | Color | Use For |
|---|---|---|
| "0" | Default | - |
| "1" | Red | Blocked |
| "2" | Orange | Priority |
| "3" | Yellow | Pending |
| "4" | Green | Completed |
| "5" | Cyan | In Progress |
| "6" | Purple | Epic |
Integration
With /retro
After /retro extracts entities, /canvas-gen can visualize:
- •Skills used → nodes
- •Agents spawned → child nodes
- •Dependencies → edges
With /next
/next shows ready issues; /canvas-gen can highlight them in canvas view.
Examples
Example 1: Current Work
code
/canvas-gen ✅ Canvas generated: obsidian-vault/canvas/beads-2026-01-26.canvas Nodes: 5 | Edges: 2 Open in Obsidian to view
Example 2: Specific Epic
code
/canvas-gen beads-epic-001 ✅ Canvas generated: obsidian-vault/canvas/epic-001.canvas Nodes: 8 | Edges: 5 Epic: "MVP Implementation" with 7 child issues
Error Handling
| Condition | Recovery |
|---|---|
| No beads issues found | Output empty canvas with note |
| bd command fails | Fallback to reading .beads/issues.jsonl |
| Invalid epic ID | Warn and show available epics |
Notes
- •Canvas files are JSON, git-tracked
- •Regenerate anytime — canvas reflects current beads state
- •Manual edits to canvas preserved if regenerating with different filename
- •Use
--outputto avoid overwriting custom layouts