# Canvas Gen

> Generate Obsidian Canvas files from beads issues. Use: /canvas-gen [epic-id]

- Skill: `demithras/canvas-gen` (Agent Skill)
- Install (CLI): `npx skillmds@latest add demithras/canvas-gen`
- Raw SKILL.md: https://api.skillmd.com/api/skills/demithras/canvas-gen/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: demithras (https://skillmd.com/u/demithras)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/demithras/canvas-gen

---


# /canvas-gen — Canvas Generation from Beads

> **TL;DR**: Auto-generate Obsidian Canvas files from beads issues for visual task tracking.

---

## Usage

```
/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

```
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

```
/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

```
/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 `--output` to avoid overwriting custom layouts

