
Canvas
- 14 installs
- 246 repo stars
- Updated April 10, 2026
- agricidaniel/claude-canvas
canvas is a Claude Code skill that acts as a Creative Director to generate fully laid-out Obsidian Canvas files such as presentations, flowcharts, and mood boards.
About
canvas generates fully laid-out Obsidian Canvas (.canvas) files from a description, including presentations, flowcharts, mood boards, knowledge graphs, storyboards, and timelines. Claude acts as a Creative Director, dispatching sub-agents for image generation, SVG diagrams, GIFs, and spatial layout, and supports 12 template archetypes and 6 layout algorithms. A developer or note-taker uses it to build visual boards inside Obsidian.
- Generates populated Obsidian Canvas files: presentations, flowcharts, mood boards, storyboards, timelines
- Claude acts as Creative Director dispatching image, SVG, GIF, and layout sub-agents
- Supports 12 template archetypes and 6 layout algorithms with auto-positioning
Canvas by the numbers
- 14 all-time installs (skills.sh)
- Ranked #1,409 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
canvas capabilities & compatibility
- Capabilities
- canvas generation · presentation generation · diagramming · layout
- Works with
- obsidian
- Use cases
- presentations · ui design · image generation
- Pricing
- Free
What canvas says it does
AI-orchestrated visual production for Obsidian Canvas.
Claude acts as Creative Director for Obsidian Canvas. Describe what you want and get a fully populated, professionally laid-out `.canvas` file.
Supports 12 template archetypes, 6 layout algorithms, and Advanced Canvas presentation mode.
npx skills add https://github.com/agricidaniel/claude-canvas --skill canvasAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 14 |
|---|---|
| repo stars | ★ 246 |
| Last updated | April 10, 2026 |
| Repository | agricidaniel/claude-canvas ↗ |
What it does
Generate populated, auto-laid-out Obsidian Canvas files (presentations, boards, diagrams) from a description.
Who is it for?
building presentations, flowcharts, mood boards, storyboards, and knowledge graphs inside Obsidian
Skip if: non-Obsidian design surfaces or single-image generation without a canvas
When should I use this skill?
the user wants to create, build, or lay out an Obsidian canvas, presentation, mood board, or flowchart
What you get
A populated, professionally laid-out .canvas file is produced from a plain description.
- .canvas files
- canvas presentations
- flowcharts and mood boards
By the numbers
- 12 template archetypes
- 6 layout algorithms
- 1200x675 presentation slides
Files
canvas: AI-Orchestrated Visual Production
Claude acts as Creative Director for Obsidian Canvas. Describe what you want and get a fully populated, professionally laid-out .canvas file.
---
Context Detection
Before any operation, determine the canvas directory:
1. If wiki/canvases/ exists in the current directory or a parent: use it (claude-obsidian vault mode).
- Media goes to
_attachments/images/canvas/
2. Otherwise: use .canvases/ in the current working directory (standalone mode).
- Media goes to
.canvases/assets/
3. Create the directory if it doesn't exist.
Default canvas: [canvas_dir]/main.canvas
---
Command Routing
| Command | Sub-skill | Description |
|---|---|---|
/canvas (no args) | (inline) | Status: list canvases, node counts, zones |
/canvas create [name] | canvas-create | Create blank or templated canvas |
/canvas create [name] from [template] | canvas-create | Create from archetype |
/canvas add [type] [content] | canvas-populate | Add node (image/text/pdf/note/link/mermaid/svg/gif/banana) |
/canvas zone [name] [color] | canvas-populate | Add group node |
/canvas connect [from] [to] [label] | canvas-populate | Add edge between nodes |
/canvas from banana | canvas-populate | Import recent AI-generated images |
/canvas layout [algorithm] | canvas-layout | Re-layout (auto/grid/dagre/radial/force/linear) |
/canvas present [topic] | canvas-present | Build presentation canvas (1200x675 slides) |
/canvas present from [notes] | canvas-present | Presentation from existing content |
/canvas generate [description] | canvas-generate | AI-orchestrated full canvas generation |
/canvas template list | canvas-template | Browse 12 archetypes |
/canvas template use [name] | canvas-template | Instantiate a template |
/canvas export [format] [path] | canvas-export | Export to PNG/SVG/PDF |
/canvas list | (inline) | List all canvases with stats |
---
Status / List (Inline Operations)
/canvas (no args)
1. Detect canvas directory (vault or standalone). 2. Find default canvas (main.canvas). 3. If exists: read JSON, count nodes by type, list zone labels. Report: "Canvas has N nodes: X images, Y text, Z files. Zones: [list]" 4. If not exists: report "No canvas found. Run /canvas create [name] to start."
/canvas list
1. Glob [canvas_dir]/*.canvas. 2. For each: read JSON, count nodes by type. 3. Report table:
main.canvas 14 nodes (8 images, 3 text, 2 file, 1 group)
design-ideas.canvas 42 nodes (30 images, 4 text, 8 groups)---
Key References
Read these references before performing canvas operations:
references/canvas-spec.md— JSON Canvas 1.0 format, coordinate system, node types, edges, colors, sizingreferences/performance-guide.md— Node limits, GIF lag, SVG gotchas, 20px grid snapping
Additional references:
references/layout-algorithms.md— 6 layout algorithms (canvas-layout)references/template-catalog.md— 12 archetypes (canvas-template)
references/presentation-spec.md— Advanced Canvas slides (canvas-present)
references/mermaid-patterns.md— Mermaid in text nodes (canvas-populate, canvas-generate)references/media-guide.md— Image/GIF/SVG integration (canvas-generate, canvas-populate)
---
Auto-Positioning Algorithm
Used by canvas-populate to place new nodes. Read references/canvas-spec.md for the full coordinate system.
def next_position(canvas_nodes, target_zone_label, new_w, new_h):
# Find zone group node
zone = next((n for n in canvas_nodes
if n.get('type') == 'group'
and n.get('label') == target_zone_label), None)
if zone is None:
# No zone: place below all content
max_y = max((n['y'] + n.get('height', 0) for n in canvas_nodes), default=-140)
return snap_grid(-400, max_y + 60)
zx, zy = zone['x'], zone['y']
zw, zh = zone['width'], zone['height']
# Nodes inside this zone (exclude groups)
inside = [n for n in canvas_nodes
if n.get('type') != 'group'
and zx <= n['x'] < zx + zw
and zy <= n['y'] < zy + zh]
if not inside:
return snap_grid(zx + 20, zy + 20)
# Find the bottom-most row: nodes whose bottom edge is closest to the zone bottom
max_bottom = max(n['y'] + n.get('height', 0) for n in inside)
# Nodes on the last row: those whose top y is within one row-height of the bottom
last_row = [n for n in inside if n['y'] + n.get('height', 0) >= max_bottom - 20]
if not last_row:
last_row = inside # fallback
rightmost_x = max(n['x'] + n.get('width', 0) for n in last_row)
next_x = rightmost_x + 40
if next_x + new_w > zx + zw:
# Overflow: new row below the current last row
return snap_grid(zx + 20, max_bottom + 20)
# Same row: align to top of the LAST row (not all nodes)
current_row_y = min(n['y'] for n in last_row)
return snap_grid(next_x, current_row_y)
def snap_grid(x, y, grid=20):
return (round(x / grid) * grid, round(y / grid) * grid)---
ID Generation
Read the canvas JSON first. Collect all existing IDs. Never reuse one.
Pattern: [type]-[content-slug]-[full-unix-timestamp]
Use the full 10-digit Unix timestamp to avoid collisions in batch operations.
Examples: img-cover-1744032823, text-note-1744032845, zone-branding-1744032901
If a collision is detected (ID already exists), append -2, -3, etc.
---
Canvas JSON Structure
The minimal valid canvas:
{
"nodes": [],
"edges": []
}Z-index rule: First node in the array renders at the bottom (background). Last node renders on top (foreground). Groups MUST come before their contained nodes so content renders in front of zone backgrounds.
Grid snapping: All x, y, width, height values should be multiples of 20.
Node limit: Warn the user if a canvas exceeds 100 nodes. Error if it exceeds 200.
---
Quality Standards (MANDATORY)
Every canvas produced by any sub-skill MUST pass these checks before reporting success. These are not optional — they are the definition of "done."
Content Quality
- NO placeholder text in any node. Replace ALL of these:
- "Describe this step" → write a real step description relevant to the title
- "YYYY-MM-DD" → use today's date or a realistic date
- "Value: 0, Target: 100" → use realistic example values
- "Content goes here" → write actual content matching the slide topic
- "Define this entity" → write a real definition
- "What happened" → write a real event description
- Every text node must contain real, useful content that a user can immediately understand
- Template instantiation is STEP 1 — writing real content into the nodes is STEP 2 (never skip it)
Layout Quality
- Minimum 80px horizontal gap between adjacent content nodes
- Minimum 60px vertical gap between adjacent content nodes
- Mind-map canvases must have radial layout (run
canvas layout radialafter instantiation) - Knowledge-graph canvases must have force layout (run
canvas layout forceafter instantiation) - Flowchart canvases should have dagre layout applied (run
canvas layout dagrefor proper hierarchy) - No overlapping nodes — run
canvas_validate.pyto confirm
Structural Quality
- Groups (zones) appear BEFORE content nodes in the array (z-index)
- All coordinates are multiples of 20 (grid snapping)
- Node count under 120 (warn at 100, error at 200)
- All file paths are vault-relative (no absolute paths)
- Edge IDs are unique, node IDs are unique
Before Reporting Success
1. Run python3 scripts/canvas_validate.py <path> — must return valid: true with 0 errors 2. Visually scan the generated JSON — are there any "Describe this" or "YYYY-MM-DD" strings remaining? 3. If the canvas has groups, verify content nodes are inside their designated zones (center-point check) 4. If the archetype needs a specific layout (mind-map→radial, kg→force), verify it was applied
---
Integration with Other Skills
banana (AI image generation):
/canvas add banana [prompt]delegates to the banana skill, then adds the result as a file node./canvas from bananareads.recent-images.txtor finds images modified in the last 10 minutes.- If banana is not installed, report gracefully: "Install the banana skill for AI image generation."
svg (diagram/chart/icon generation):
/canvas add svg [description]delegates to the svg skill, then adds the SVG as a file node.- SVGs render as
<img>in Obsidian — no interactivity. Must includeviewBoxfor proper scaling.
claude-gif-* (GIF generation/editing):
/canvas add gif [description]delegates to the gif skill, then adds as a file node.- Performance warning: limit to 3 GIFs per canvas, cap dimensions at 480px width.
Mermaid (native in text nodes):
- Mermaid code blocks render natively in Obsidian text nodes. No external skill needed.
- Wrap in triple-backtick mermaid code fence inside a text node.
Obsidian Canvas JSON Specification
Canvas files are JSON with two top-level keys: nodes (array) and edges (array). Obsidian reads and writes them as UTF-8 JSON files with .canvas extension.
This reference aligns with the JSON Canvas 1.0 open specification. All structures support arbitrary additional fields ([key: string]: any) for forward compatibility. Obsidian will preserve unknown fields when reading and writing canvas files.
ID format: Use descriptive IDs with timestamps: [type]-[content-slug]-[unix-timestamp] (e.g., img-cover-1744032823). Obsidian also accepts 16-character lowercase hex IDs. Both are valid JSON Canvas.
---
Coordinate System
x increases →
┌─────────────────────────────────
│ (-920, -2400) (0, -2400)
│
y │ (-920, 0) (0, 0) ← origin
↓ │
│ (-920, 540) (500, 540)- Origin (0, 0) is the center of the canvas viewport.
- x increases rightward. Negative x = left of center.
- y increases downward. Negative y = above center.
- Node
xandyare the top-left corner of the node, not the center. - Obsidian pans to fit all nodes on first open. No saved viewport state.
- Grid snapping: Obsidian snaps to ~20px increments. Align generated coordinates to multiples of 20.
---
Node Types
Common Fields (All Nodes)
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | yes | Unique within the canvas |
type | string | yes | "text", "file", "link", "group" |
x | integer | yes | Top-left corner x (pixels) |
y | integer | yes | Top-left corner y (pixels) |
width | integer | yes | Width in pixels |
height | integer | yes | Height in pixels |
color | string | no | Preset "1"-"6" or hex "#FF0000" |
Text Node
Renders markdown content as a styled card. Supports full Obsidian Flavored Markdown: headings, bold/italic, wikilinks, embeds, callouts, code blocks, LaTeX math, Mermaid diagrams, tables, task lists, tags, footnotes, and Dataview queries.
{
"id": "text-title-4821",
"type": "text",
"text": "# Heading\n\nParagraph with **bold** and `code`.",
"x": -400, "y": -300, "width": 400, "height": 120, "color": "6"
}text: markdown string. Use\nfor newlines.- Minimum readable size: width >= 200, height >= 60.
coloris optional. Omit for default (no color).
File Node
Renders an image, PDF, markdown note, or other vault file inline.
{
"id": "img-cover-7823",
"type": "file",
"file": "_attachments/images/example.png",
"x": -900, "y": -100, "width": 420, "height": 236
}file: vault-relative path (not absolute, not~/).subpath(optional): heading or block reference, starts with#.- Supported inline rendering:
.png.jpg.webp.gif(animated, auto-plays).pdf.md.canvas.mp4.webm.ogv(video with controls).mp3.flac.wav.ogg(audio player) - SVG renders as
<img>tag — no interactivity, no hover effects. Must includeviewBoxfor proper scaling. - Images use
object-fit: contain. Not upscaled beyond native resolution. - No
colorfield for file nodes: color is ignored.
Group Node (Zone)
A labeled rectangular region. Does not clip or contain nodes — purely visual guide. Nodes placed "inside" a group are just positioned within its bounding box. Moving a group in Obsidian moves all spatially-contained nodes.
{
"id": "zone-branding-3391",
"type": "group",
"label": "Brand Identity",
"x": -920, "y": -880, "width": 1060, "height": 290, "color": "6",
"background": "_attachments/images/grid-bg.png",
"backgroundStyle": "cover"
}label: shown at the top of the group box.color: colors the group border and label.background(optional): vault-relative path to background image.backgroundStyle(optional):"cover"(fill, crop) |"ratio"(fit, preserve) |"repeat"(tile).
Link Node
Renders a web URL as an embedded preview card with Open Graph data.
{
"id": "link-karpathy-2233",
"type": "link",
"url": "https://github.com/karpathy",
"x": 200, "y": -300, "width": 400, "height": 120
}url: must be a validhttps://URL.
---
Edges
Connections between nodes. Rendered as Bezier curves by default.
{
"id": "e-hub-cidx",
"fromNode": "hub",
"toNode": "c-idx",
"fromSide": "right",
"fromEnd": "none",
"toSide": "left",
"toEnd": "arrow",
"label": "concepts",
"color": "5"
}Required: id, fromNode, toNode. Everything else is optional.
| Field | Values | Default | Notes |
|---|---|---|---|
fromSide / toSide | "top" "bottom" "left" "right" | auto-calculated | Omit for better auto-routing |
fromEnd | "none" "arrow" | "none" | End-cap on source side |
toEnd | "none" "arrow" | "arrow" | End-cap on target side (asymmetric default!) |
label | string | — | Text shown on the edge |
color | "1"-"6" or hex | — | Edge color |
Pro tip: Omitting fromSide/toSide lets Obsidian auto-route edges dynamically. This often produces better results than manual specification.
---
Color Reference
| Code | Color | Hex (approx) | Use case |
|---|---|---|---|
"1" | Red / Tomato | #e03e3e | Warnings, archive |
"2" | Orange | #d09035 | Active work |
"3" | Yellow / Gold | #d0a023 | WIP, notes |
"4" | Green / Teal | #448361 | Content, sources |
"5" | Blue / Cyan | #3ea7d3 | Navigation, info |
"6" | Purple / Violet | #9063d2 | Title, identity |
Colors are strings, not integers: "1" not 1. Specific RGB values are theme-dependent. These map to CSS variables (--canvas-color-1 through --canvas-color-6).
Omit color entirely for the default (no border color, transparent label).
---
Image Sizing Guidelines
Calculate from actual image dimensions using PIL or identify:
python3 -c "from PIL import Image; img=Image.open('path.png'); print(img.width, img.height)"
# or
identify -format '%w %h' path.png| Aspect ratio | Condition | Canvas width | Canvas height |
|---|---|---|---|
| 16:9 (wide) | ratio 1.6–2.0 | 420 | 236 |
| 2:1 (ultra wide) | ratio > 2.0 | 440 | 220 |
| 4:3 | ratio 1.2–1.6 | 380 | 285 |
| 1:1 (square) | ratio 0.9–1.1 | 280 | 280 |
| 3:4 | ratio 0.6–0.9 | 240 | 320 |
| 9:16 (portrait) | ratio < 0.6 | 200 | 356 |
| any | 400 | 520 | |
| Unknown | fallback | 320 | 240 |
---
Undocumented Behaviors
These are not in the JSON Canvas 1.0 spec but are confirmed Obsidian behaviors:
1. Z-index = array order: First node in nodes array renders at bottom, last on top. Selecting a card moves it to end of array. 2. Group containment is spatial only: No parent-child relationship in JSON. If node coordinates fall inside group bounds, it appears inside. 3. Canvas links don't create backlinks: Edges are visual-only, don't appear in Graph View. The Advanced Canvas plugin fixes this. 4. Placeholder rendering: At far zoom levels, nodes collapse to colored rectangles for performance. 5. Nested canvases: .canvas files render as static schematic previews (since v1.1.5), not interactive.
---
Common Mistakes
- Wrong path format: use
_attachments/images/file.pngnot/home/user/...or~/... - ID collision: always read existing IDs before generating a new one
- Negative y confusion:
y: -2400is ABOVEy: -1000(more negative = higher up) - Group does not clip: positioning a node "inside" a group is just bounding box overlap
- Missing height on text nodes: Obsidian may clip text if height too small. Use height >= content-lines x 24.
- Color as integer: Use
"1"not1— colors are strings - Specifying edge sides unnecessarily: Omit fromSide/toSide for auto-routing unless flow direction matters
---
Full Example: Two-Zone Canvas
{
"nodes": [
{
"id": "zone-logos",
"type": "group",
"label": "Logos & Icons",
"x": -920, "y": -2200, "width": 1800, "height": 320, "color": "6"
},
{
"id": "title-0001",
"type": "text",
"text": "# Brand Reference\n\n**AI Marketing Hub** visual assets",
"x": -920, "y": -2440, "width": 560, "height": 180, "color": "6"
},
{
"id": "img-logo-pro",
"type": "file",
"file": "_attachments/images/example.png",
"x": -900, "y": -2180, "width": 420, "height": 236
},
{
"id": "img-icon-free",
"type": "file",
"file": "_attachments/images/example-icon.png",
"x": -440, "y": -2180, "width": 280, "height": 280
},
{
"id": "zone-covers",
"type": "group",
"label": "Skill Covers",
"x": -920, "y": -1820, "width": 1800, "height": 340, "color": "3"
},
{
"id": "img-seo",
"type": "file",
"file": "_attachments/images/example-cover.png",
"x": -900, "y": -1800, "width": 420, "height": 236
}
],
"edges": []
}Note: Groups come before their contained nodes in the array (z-index ordering).
Layout Algorithms Reference
Six algorithms for re-arranging canvas nodes. Each is optimized for a different canvas archetype.
Run via: python3 scripts/canvas_layout.py <canvas> <algorithm> [options]
---
Algorithm Selection Guide
| Algorithm | Best For | Edge Behavior | Parameters |
|---|---|---|---|
| grid | Galleries, mood boards, comparisons | Ignores edges | --columns N, `--sort-by type\ |
| dagre | Flowcharts, org charts, processes | Follows edge direction | `--direction TB\ |
| radial | Mind maps, concept maps, topic exploration | Builds rings from center | --center node-id |
| force | Knowledge graphs, entity relationships | Attracts connected, repels unconnected | --iterations N |
| linear | Timelines, sequences, step-by-step | Ignores edges | `--axis horizontal\ |
| auto | When unsure | Analyzes content + edges | (none — auto-detects) |
---
Auto-Detection Heuristics
The auto algorithm inspects content and edges to pick the best layout:
1. >60% file nodes + few edges → grid (gallery/mood board pattern) 2. Zero edges → grid (no relationship data to layout) 3. One node has >40% of all connections → radial (hub-and-spoke) 4. Clear hierarchy (pure source nodes) + edges → dagre (flowchart) 5. Dense edges (>1 edge per node) → force (network graph) 6. Fallback → dagre (most versatile structured layout)
---
Grid Layout
Arranges nodes in rows and columns. Nodes are centered within uniform cells.
Auto-columns: ceil(sqrt(node_count)), clamped to 2-6.
Cell sizing: Uses the maximum node width + 60px gap horizontally, maximum height + 40px gap vertically. Each node is centered within its cell.
Sort options:
type(default): Groups by node type (text → file → link)size: Largest nodes first (Pinterest masonry feel)
┌──────┐ ┌──────┐ ┌──────┐
│ A │ │ B │ │ C │
└──────┘ └──────┘ └──────┘
┌──────┐ ┌──────┐ ┌──────┐
│ D │ │ E │ │ F │
└──────┘ └──────┘ └──────┘---
Dagre Layout (Hierarchical / Sugiyama)
Assigns nodes to layers based on edge direction, then positions within layers.
Layer assignment: BFS from root nodes (nodes with no incoming edges). Each edge increases the layer by 1.
Directions:
TB(default): Top to bottom — classic flowchartLR: Left to right — process flowBT: Bottom to top — org chart (inverted)RL: Right to left — reverse flow
Within-layer positioning: Nodes in the same layer are evenly spaced perpendicular to the flow direction. Centering reduces edge crossings.
TB direction: LR direction:
┌───┐ ┌───┐
│ A │ │ A │──→ ┌───┐
└─┬─┘ └───┘ │ C │
┌───┴───┐ ┌───┐ └───┘
┌─┴─┐ ┌──┴─┐ │ B │──→ ┌───┐
│ B │ │ C │ └───┘ │ D │
└───┘ └────┘ └───┘---
Radial Layout
Expands outward from a center node in concentric rings.
Center selection: Node with the most connections (edges). Override with --center node-id.
Ring assignment: BFS from center. Ring 0 = center, Ring 1 = direct neighbors, etc.
Positioning: Each ring has radius = 300px × ring_number. Nodes are evenly distributed around the ring using angle_step = 2π / node_count.
○ C
╱
○ B ─── ● Center ─── ○ D
╲
○ E---
Force-Directed Layout (Fruchterman-Reingold)
Physics simulation: connected nodes attract, all nodes repel. Iterates until stable.
Forces:
- Repulsive (all pairs):
k² / distance— pushes nodes apart - Attractive (edges only):
distance² / k— pulls connected nodes together - k =
sqrt(area / node_count)— optimal spacing
Temperature: Starts high (allows large movements), cools by 5% per iteration. At 100 iterations, the layout is typically stable.
Performance: O(n²) per iteration. For >50 nodes, consider reducing iterations to 50.
○──────○
╱ ╲ ╱
○ ○──○
╲ ╱
○---
Linear Layout (Timeline)
Places nodes in a single line along one axis.
Horizontal (default): Left to right, centered vertically. Good for timelines. Vertical: Top to bottom, centered horizontally. Good for step sequences.
Ordering: Preserves current position order on the layout axis. Nodes are sorted by their current x (horizontal) or y (vertical) coordinate before placement.
Spacing: Node width/height + 60px horizontal gap or 40px vertical gap.
Horizontal: ┌──┐ ┌──┐ ┌──┐ ┌──┐
│A │──│B │──│C │──│D │
└──┘ └──┘ └──┘ └──┘
Vertical: ┌──┐
│A │
└──┘
┌──┐
│B │
└──┘
┌──┐
│C │
└──┘---
Group Preservation
All algorithms preserve group (zone) membership:
1. Before layout: record which content nodes are inside which groups 2. During layout: only content nodes are repositioned 3. After layout: groups are refitted to tightly wrap their member nodes with 20px padding (plus 40px top for the label)
Groups that had no members remain at their original position.
---
Common Parameters
| Parameter | Default | Description |
|---|---|---|
--dry-run | false | Calculate layout without writing changes |
--columns | auto | Grid columns (grid only) |
--direction | TB | Flow direction (dagre only) |
--center | auto | Center node ID (radial only) |
--axis | horizontal | Layout axis (linear only) |
--iterations | 100 | Simulation steps (force only) |
--sort-by | type | Sort order (grid only) |
All outputs are JSON:
{
"success": true,
"algorithm": "dagre",
"auto_detected": false,
"nodes_moved": 12,
"total_nodes": 15,
"groups_preserved": 3,
"backup": "canvas.canvas.bak",
"dry_run": false
}| Field | Type | Description |
|---|---|---|
success | bool | Whether the layout completed without errors |
algorithm | string | The algorithm that was actually applied |
auto_detected | bool | Whether auto was used and this algorithm was chosen |
nodes_moved | int | Number of content nodes that changed position |
total_nodes | int | Total content nodes (excludes groups) |
groups_preserved | int | Number of groups that were refitted |
backup | string/null | Path to backup file (null if dry-run) |
dry_run | bool | Whether changes were actually written |
Media Integration Guide
How to generate and place images, GIFs, SVGs, and video thumbnails on Obsidian Canvas using external skills.
---
Integration Architecture
User request → canvas-generate/canvas-populate
↓
┌───────┼───────┐
│ │ │
/banana /svg /gif
(Gemini) (Python) (Veo/Remotion)
│ │ │
↓ ↓ ↓
_attachments/images/canvas/
.canvases/assets/
↓
file node on canvas---
Image Generation via /banana
Skill: /banana (requires nanobanana-mcp MCP server) Output: PNG at ~/Documents/nanobanana_generated/
Workflow
1. Check if banana skill is available. If not: "Install /banana for AI image generation." 2. Generate image with prompt:
/banana generate "[prompt]" --size 1024x10243. Copy the generated image to the canvas media directory. 4. Detect aspect ratio and compute canvas node dimensions. 5. Add as file node to the canvas.
Prompt Patterns for Canvas
| Context | Prompt Pattern |
|---|---|
| Presentation hero | [topic], presentation slide style, clean, professional, minimal |
| Mood board | [aesthetic], mood board reference, high quality photography |
| Dashboard icon | [concept] icon, flat design, simple, white background |
| Storyboard scene | [scene description], cinematic, 16:9 aspect ratio |
| Gallery showcase | [subject], product photography, studio lighting |
Sizing After Generation
Map generated image dimensions to canvas file node sizes using the aspect ratio table in canvas-spec.md:
| Image Dimensions | Canvas Width | Canvas Height |
|---|---|---|
| 1024×1024 (1:1) | 280 | 280 |
| 1920×1080 (16:9) | 420 | 236 |
| 1080×1920 (9:16) | 200 | 356 |
| 1024×768 (4:3) | 380 | 285 |
---
SVG Generation via /svg
Skill: /svg (sub-skills: svg-diagram, svg-chart, svg-icon) Output: SVG file in project directory
Workflow
1. Generate SVG with the appropriate sub-skill:
/svg diagram— flowcharts, architecture diagrams/svg chart— bar, line, pie, radar charts/svg icon— icons and symbol sprites
2. Copy the SVG to the canvas media directory. 3. Add as file node to the canvas.
SVG-Specific Constraints
- SVGs render as
<img>in Obsidian — no interactivity - Must include `viewBox` attribute for proper scaling
- CSS animations may not render
currentColordoes not resolve (use explicit colors)- File node sizing: width=400, height based on viewBox aspect ratio
Recommended Sizing
# Compute canvas dimensions from SVG viewBox
viewbox = "0 0 800 600" # width=800, height=600
vb_w, vb_h = 800, 600
ratio = vb_w / vb_h
canvas_w = 400
canvas_h = round(canvas_w / ratio)---
GIF Generation via /gif Skills
Skills: /claude-gif-generate, /claude-gif-create, /claude-gif-convert Output: GIF file
Performance Limits
| Constraint | Limit | Reason |
|---|---|---|
| Max GIFs per canvas | 3 | GPU/CPU overhead from continuous rendering |
| Max GIF width | 480px | Prevents frame drops during pan/zoom |
| Max GIF file size | 2MB | Load lag on large files |
GIF Canvas Sizing
GIFs use the same aspect ratio table as images. Common GIF dimensions:
| GIF Dimensions | Canvas Width | Canvas Height |
|---|---|---|
| 480×270 (16:9) | 420 | 236 |
| 480×480 (1:1) | 280 | 280 |
| 320×240 (4:3) | 380 | 285 |
When to Use GIFs vs. Static Images
- Use GIF: Animated demos, loading indicators, attention-grabbing hero images
- Use PNG/JPG: Everything else (better performance, smaller files)
- Use SVG: Diagrams, charts, icons (vector quality, tiny files)
---
Mermaid Diagrams (Native)
Mermaid renders natively in text nodes — no external file or skill needed. See mermaid-patterns.md for all diagram types and sizing recommendations.
When to Use Mermaid vs. /svg
| Use Case | Mermaid | /svg |
|---|---|---|
| Quick flowchart on canvas | Yes | Overkill |
| Styled architecture diagram | No | Yes |
| Data chart with custom colors | No | Yes |
| Live-editable in Obsidian | Yes | No |
---
Media Directory Convention
| Context | Media Directory | Example Path |
|---|---|---|
| Vault mode | _attachments/images/canvas/ | _attachments/images/canvas/hero.png |
| Standalone mode | .canvases/assets/ | .canvases/assets/hero.png |
All file node paths in canvas JSON must be vault-relative (not absolute). Copy external files to the media directory before adding to canvas.
---
Graceful Degradation
When a media skill is not installed:
| Skill | Fallback |
|---|---|
/banana | "Install the banana skill for AI image generation. Add images manually with /canvas add image." |
/svg | Use Mermaid in text nodes for diagrams. For charts, embed data tables in text nodes. |
/gif | Use static images instead. "Install gif skills for animated content." |
Never fail silently. Always inform the user what's missing and how to work around it.
Mermaid Patterns for Canvas Text Nodes
Mermaid diagrams render natively in Obsidian canvas text nodes — no external tools or plugins needed. Wrap the code in a fenced code block inside a text node's text field.
---
How to Embed Mermaid in Canvas
Create a text node with the Mermaid code inside triple backticks:
{
"id": "text-diagram-1744032823",
"type": "text",
"text": "```mermaid\ngraph LR\n A[Start] --> B[Process]\n B --> C[End]\n```",
"x": 0, "y": 0,
"width": 500, "height": 400,
"color": "5"
}Sizing recommendations:
- Minimum width: 500px (Mermaid needs horizontal space)
- Minimum height: 400px (most diagrams need vertical space)
- Complex diagrams (7+ nodes): use 600-700px wide, 500-600px tall
- Mermaid renders INSIDE the text node bounds — if the node is too small, the diagram is clipped or overflows visually
- Always add 40-60px vertical padding below the heading text above the mermaid block
- Use color
"5"(cyan) for diagram nodes to distinguish from text cards
---
Supported Diagram Types
Flowchart (graph)
graph TD
A[Input] --> B{Decision}
B -->|Yes| C[Process A]
B -->|No| D[Process B]
C --> E[Output]
D --> ECanvas sizing: width=500, height=400 Best for: Process flows, decision trees, system diagrams
Sequence Diagram
sequenceDiagram
participant U as User
participant S as Server
participant D as Database
U->>S: Request
S->>D: Query
D-->>S: Results
S-->>U: ResponseCanvas sizing: width=500, height=350 Best for: API flows, interaction patterns, timing
Gantt Chart
gantt
title Project Timeline
dateFormat YYYY-MM-DD
section Phase 1
Research :a1, 2026-01-01, 14d
Design :a2, after a1, 7d
section Phase 2
Development :b1, after a2, 21d
Testing :b2, after b1, 14dCanvas sizing: width=600, height=300 Best for: Project timelines, sprint planning, scheduling
Pie Chart
pie title Traffic Sources
"Organic" : 45
"Paid" : 25
"Social" : 20
"Direct" : 10Canvas sizing: width=400, height=350 Best for: Proportional data, simple breakdowns
State Diagram
stateDiagram-v2
[*] --> Draft
Draft --> Review
Review --> Approved
Review --> Draft : Revisions
Approved --> Published
Published --> [*]Canvas sizing: width=500, height=350 Best for: Status flows, lifecycle models, workflow states
Entity-Relationship Diagram
erDiagram
USER ||--o{ ORDER : places
ORDER ||--|{ LINE_ITEM : contains
PRODUCT ||--o{ LINE_ITEM : "is in"
USER {
string name
string email
}
ORDER {
int id
date created
}Canvas sizing: width=600, height=400 Best for: Database schemas, data modeling
Git Graph
gitGraph
commit
branch feature
commit
commit
checkout main
merge feature
commitCanvas sizing: width=500, height=300 Best for: Branching strategies, release flows
---
Performance Notes
- Large Mermaid diagrams (20+ nodes) may overflow their text node bounds
- Complex diagrams (50+ elements) cause rendering lag
- Text node height should be generous — Obsidian does not auto-resize for Mermaid
- Mermaid renders on every zoom/pan — keep diagrams under 30 elements for smooth canvas interaction
---
When to Use Mermaid vs. SVG
| Scenario | Use Mermaid | Use SVG (/svg skill) |
|---|---|---|
| Quick flowchart | Yes | No — overkill |
| Complex architecture diagram | No — limited styling | Yes |
| Data visualization with colors | No — limited palette | Yes |
| Inline in a text card | Yes — native rendering | No — requires file node |
| Needs custom fonts/sizing | No | Yes |
| Live-editable in Obsidian | Yes — edit the text | No — regenerate file |
Canvas Performance Guide
Constraints and limits for generating Obsidian Canvas files that perform well.
---
Node Limits
| Threshold | Impact | Action |
|---|---|---|
| <50 nodes | Smooth on all hardware | No concerns |
| 50-100 nodes | Fine on modern hardware | Monitor |
| 100-200 nodes | Lag on mid-range systems | Warn the user |
| 200+ nodes | Severe lag, panning/zooming breaks | Error — refuse to generate |
Recommendation: Target 15-30 visible nodes per viewport for comprehension. Keep total under 120 nodes for broad hardware compatibility.
If a canvas exceeds 100 nodes, suggest splitting into sub-canvases linked via file nodes (nested canvas preview).
---
Minimum Spacing Between Nodes
Nodes must have adequate spacing to prevent visual overlap and clipping:
| Between | Minimum Gap | Recommended |
|---|---|---|
| Adjacent content nodes (horizontal) | 80px | 100px |
| Adjacent content nodes (vertical) | 60px | 80px |
| Node and zone boundary (padding) | 20px | 30px |
| Zone label area (top of zone) | 60px | 60px |
| Rows of different content types | 60px | 80px |
Mermaid diagram nodes need extra space — the rendered diagram often exceeds the text node bounds. Use minimum 600x500 for flowcharts, 500x400 for simpler diagrams.
Overlap prevention: Run canvas_validate.py after generation — it detects node overlaps >10% and warns.
---
Grid Snapping
All generated coordinates (x, y, width, height) must be multiples of 20.
def snap(value, grid=20):
return round(value / grid) * gridObsidian's native grid is ~20px. Misaligned coordinates cause visual jitter when dragging nodes.
---
Z-Index (Array Order)
The nodes array order determines rendering order:
- First node renders at the bottom (background)
- Last node renders on top (foreground)
Rule: Always place group nodes (zones) before their contained nodes in the array. This ensures content renders on top of zone backgrounds.
{
"nodes": [
{"id": "zone-a", "type": "group", ...},
{"id": "zone-b", "type": "group", ...},
{"id": "text-in-zone-a", "type": "text", ...},
{"id": "img-in-zone-b", "type": "file", ...}
]
}---
GIF Performance
Animated GIFs play automatically in canvas and continue rendering even when scrolled off-screen.
| Constraint | Limit | Reason |
|---|---|---|
| Max GIFs per canvas | 3 | Each GIF consumes GPU/CPU continuously |
| Max GIF width | 480px | Larger GIFs cause frame drops during pan/zoom |
| Max GIF file size | 2MB | Larger files cause load lag |
Gotcha: Pasting GIFs from clipboard loses animation. Must use drag-and-drop or file node reference.
---
SVG Rendering
SVGs render as <img> tags in canvas — no interactivity, no hover effects, no clickable links.
Requirements:
- Must include
viewBoxattribute for proper scaling - CSS animations may or may not render (inconsistent)
- For interactive SVGs, the only workaround is iframe embedding (not recommended in canvas)
Recommendation: Generate SVGs with explicit viewBox="0 0 width height". Use currentColor for theme compatibility (though it won't resolve in <img> context — default to dark colors).
---
Image Resolution
Obsidian does not upscale images beyond native resolution. Extra space shows as blank area.
| Guideline | Max recommended |
|---|---|
| Image width | 2000px |
| Image height | 2000px |
| Total image file size | 5MB |
For AI-generated images (banana), 1024x1024 or 1920x1080 is optimal.
---
Text Node Size
| Constraint | Limit |
|---|---|
| Max characters in one text node | ~5000 (performance degrades at 26K+) |
| Min readable width | 200px |
| Min readable height | 60px |
| Height rule of thumb | content-lines x 24px |
---
Canvas File Size
| Threshold | Impact |
|---|---|
| <100KB | Fast load |
| 100-500KB | Acceptable |
| 500KB-1MB | Slow to open |
| 1MB+ | May cause Obsidian hangs |
File size grows with text node content and the number of nodes. Image data is NOT stored in the canvas file (only paths).
---
Recommended Plugin
Canvas Performance Patch (Qbject) fixes a media embed re-rendering bug. CSS workaround: setting canvas wrapper size to 1000% prevents node loading/unloading at viewport edges.
Advanced Canvas (Developer-Mike) adds Graph View integration, PNG/SVG export, and presentation mode.
---
Performance Checklist
Before writing a canvas file, verify:
- [ ] Total nodes < 200 (warn at 100)
- [ ] All coordinates are multiples of 20
- [ ] Groups appear before contained nodes in array
- [ ] GIFs: max 3 per canvas, max 480px width
- [ ] SVGs: all have
viewBoxattribute - [ ] Text nodes: none exceed 5000 characters
- [ ] No absolute paths in file nodes (vault-relative only)
Presentation Mode Specification
Build slide-deck canvases that work with the Advanced Canvas plugin's presentation mode.
---
How Presentation Mode Works
Advanced Canvas adds arrow-key navigation to Obsidian Canvas. Each "slide" is a group node connected to the next slide by an edge. The user presses arrow keys to zoom/pan between slides along the edge chain.
Requirements:
- Advanced Canvas plugin installed in Obsidian (515K+ downloads, actively maintained)
- Each slide is a
groupnode (zones) - Slides are connected by edges in sequence (slide 1 → slide 2 → slide 3...)
- Slide content (text, images) is placed inside the slide's group bounds
---
Slide Dimensions
| Style | Group Width | Group Height | Use Case |
|---|---|---|---|
| Deck (16:9) | 1200 | 675 | Standard presentations |
| Storyboard (16:9) | 1920 | 1080 | Video planning with annotations |
| Compact (4:3) | 960 | 720 | Dense content, smaller screens |
Recommended: 1200×675 for most presentations. Obsidian auto-fits each slide group to the viewport on navigation.
---
Slide Structure
Each slide is a group node containing child nodes:
{
"id": "zone-slide-1-1744032823",
"type": "group",
"label": "Slide 1: Introduction",
"x": 0, "y": 0,
"width": 1200, "height": 675,
"color": "4"
}Content Inside a Slide
Place nodes spatially inside the group's bounds. Common patterns:
Title slide:
┌─────────────────────────────────┐
│ Slide 1: Title │
│ ┌───────────────────────────┐ │
│ │ # Presentation Title │ │
│ │ Author • Date │ │
│ │ │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘Content slide (text + image):
┌─────────────────────────────────┐
│ Slide 2: Key Finding │
│ ┌──────────┐ ┌────────────┐ │
│ │ ## Title │ │ │ │
│ │ • Point 1 │ │ image │ │
│ │ • Point 2 │ │ │ │
│ │ • Point 3 │ │ │ │
│ └──────────┘ └────────────┘ │
└─────────────────────────────────┘Full-text slide:
┌─────────────────────────────────┐
│ Slide 3: Deep Dive │
│ ┌───────────────────────────┐ │
│ │ ## Section Title │ │
│ │ Detailed content with │ │
│ │ multiple paragraphs, │ │
│ │ callouts, and lists. │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘Content Node Sizing Inside Slides
| Content Type | Width | Height | Position |
|---|---|---|---|
| Full-width text | slide_w - 60 (1140) | varies | x+30, y+60 |
| Half-width text | slide_w/2 - 40 (560) | varies | x+20, y+60 |
| Half-width image | slide_w/2 - 40 (560) | auto | x+slide_w/2+10, y+60 |
| Caption text | slide_w - 60 (1140) | 60 | x+30, y+slide_h-80 |
---
Slide Navigation Edges
Connect slides sequentially with edges. Advanced Canvas follows edge chains for navigation.
{
"id": "e-slide-1-2-1744032823",
"fromNode": "zone-slide-1-1744032823",
"toNode": "zone-slide-2-1744032823",
"toEnd": "arrow"
}Rules:
- Omit
fromSide/toSidefor auto-routing - Use
toEnd: "arrow"to show flow direction - Edges must form a single linear chain (no branching for standard presentations)
- For branching presentations: create multiple edge paths from decision slides
---
Slide Layout
Vertical Stack (Recommended)
Stack slides top-to-bottom with consistent gaps:
y=0: [Slide 1: Title] (1200 × 675)
│
y=775: [Slide 2: Problem] (1200 × 675)
│
y=1550: [Slide 3: Solution] (1200 × 675)
│
y=2325: [Slide 4: Results] (1200 × 675)Gap between slides: 100px (y_next = y_prev + 675 + 100 = y_prev + 775).
Horizontal Flow
For storyboard-style presentations, arrange left-to-right:
x=0 x=1300 x=2600
[Slide 1] → [Slide 2] → [Slide 3]Gap: 100px (x_next = x_prev + 1200 + 100 = x_prev + 1300).
---
Script Annotation Column (Optional)
For video storyboards and speaker notes, add a text column to the right of each slide:
┌──────────────────┐ ┌────────────┐
│ Slide 1 │ │ SCRIPT │
│ [visual content]│ │ Speaker │
│ │ │ notes and │
│ │ │ timing │
└──────────────────┘ └────────────┘Annotation sizing: width=500, height=slide_height, x=slide_x+slide_w+40
This mirrors the youtube-explainer canvas pattern in claude-obsidian.
---
Color Coding for Slides
| Slide Type | Color | Use |
|---|---|---|
| Title/Intro | "6" (purple) | Opening slide |
| Content | "4" (green) | Standard content |
| Key Finding | "5" (cyan) | Important data/insight |
| Warning/Risk | "1" (red) | Problems, risks |
| Action Item | "2" (orange) | Next steps, todos |
| Summary/Close | "6" (purple) | Closing slide |
---
Standard Slide Deck Structure
A typical presentation follows this pattern:
1. Title Slide — Project name, author, date (color: 6) 2. Agenda/Overview — What will be covered (color: 4) 3. Context/Problem — Why this matters (color: 4) 4. Content Slides — 2-4 slides of findings/features (color: 4/5) 5. Key Insight — The main takeaway (color: 5) 6. Next Steps — Action items (color: 2) 7. Questions/Close — Q&A or closing (color: 6)
Target: 6-10 slides. More than 12 slides risks performance issues and attention loss.
---
Performance Notes
- Keep total node count under 120 (slides × ~4 nodes per slide = 48 for 12 slides)
- Limit images to 1-2 per slide (large images cause lag during transitions)
- Mermaid diagrams in text nodes work well for data slides
- GIFs play during presentation but consume GPU — limit to 1 per deck
- SVG diagrams are lightweight and recommended for charts
Template Catalog
12 canvas archetype templates for common visual patterns. Each produces a ready-to-use .canvas file with proper layout, zones, and placeholder content.
Run via: python3 scripts/canvas_template.py <template> <output> --param key=value List all: python3 scripts/canvas_template.py --list
---
Archetypes
presentation
Layout: linear-vertical | Slides: 1200x675 groups connected by edges Use: Slide decks for Advanced Canvas plugin (arrow-key navigation) Params: slide_count (default 6) Next step: Add content to each slide, generate hero images with /canvas add banana
flowchart
Layout: linear-vertical → auto-applies dagre after instantiation | Nodes: Sequential text cards with edges Use: Process documentation, decision flows Params: step_count (default 5) Next step: Edit step text to describe your actual process
mind-map
Layout: grid → auto-applies radial after instantiation | Nodes: Center + branch cards Use: Brainstorming, idea exploration, concept mapping Params: branch_count (default 5) Next step: Edit branches, add sub-branches, run /canvas layout radial --center [center-id]
gallery
Layout: grid | Nodes: Image placeholder text cards in a title zone Use: Image showcases, screenshot collections, visual portfolios Params: image_count (default 9), columns (default 3) Next step: Replace placeholders with /canvas add image or /canvas add banana
dashboard
Layout: grid | Nodes: Header + metrics zone + metric cards + status zone Use: Project status boards, KPI tracking, monitoring views Params: metric_count (default 4) Next step: Update metric values, add charts with /canvas add svg or /canvas add mermaid
storyboard
Layout: linear-horizontal | Nodes: Scene cards with visual/audio/duration fields Use: Video planning, animation sequences, narrative design Params: scene_count (default 6) Next step: Fill in scene details, add reference images with /canvas add image
knowledge-graph
Layout: grid → auto-applies force after instantiation | Nodes: Entity cards Use: Concept mapping, entity relationships, domain modeling Params: entity_count (default 8) Next step: Edit entities, add edges with /canvas connect, run /canvas layout force
mood-board
Layout: grid | Nodes: Title card + inspiration zone + image placeholders Use: Creative direction, design inspiration, aesthetic exploration Params: image_count (default 8) Next step: Replace placeholders with images via /canvas add banana for AI generation
timeline
Layout: linear-horizontal | Nodes: Event cards with date/description fields Use: Project timelines, historical events, release schedules Params: event_count (default 6) Next step: Fill in dates and descriptions, add milestone markers
comparison
Layout: grid | Nodes: Two option zones + criteria cards Use: Feature comparisons, decision analysis, A/B evaluation Params: criteria_count (default 4) Next step: Fill in criteria for each option, add summary/winner card
kanban
Layout: grid | Nodes: Todo/Doing/Done zones + task cards Use: Task management, sprint boards, workflow tracking Params: cards_per_column (default 3) Next step: Add task cards with /canvas add text, drag between columns in Obsidian
project-brief
Layout: linear-vertical | Nodes: Hero zone + objectives + deliverables zones Use: Project kickoff, scope documents, client briefs Params: objective_count (default 3) Next step: Fill in project details, add timeline with /canvas add mermaid gantt chart
---
Common Parameters
All templates accept:
| Parameter | Type | Description |
|---|---|---|
title | string | Canvas title (replaces $title in templates) |
color_title | "1"-"6" | Color for title/header elements (default: "6" purple) |
color_body | "1"-"6" | Color for body content zones (default: "4" green) |
color_accent | "1"-"6" | Color for accent/highlight elements (default: "5" cyan) |
---
Creating Custom Templates
Save any canvas as a template by extracting its structure into a JSON file in templates/:
{
"name": "Custom Template",
"description": "What this template is for",
"layout": "grid|linear-vertical|linear-horizontal",
"defaults": {
"item_count": 5,
"color_title": "6",
"color_body": "4",
"color_accent": "5"
},
"node_templates": [
{
"role": "item",
"type": "text",
"repeat": "$item_count",
"repeat_default": 5,
"text": "## Item {n}",
"width": 300,
"height": 120,
"color": "$color_body"
}
],
"edge_templates": [
{
"from_role": "item",
"to_role": "item",
"pattern": "sequential"
}
]
}Template variables: $title, $color_title, $color_body, $color_accent, {n} (repeat index). Repeat values: "$param_name" references a template parameter, "repeat_default" is the fallback. Edge patterns: "sequential" (n[i]→n[i+1]) or "broadcast" (n[0]→all others).
Related skills
FAQ
What canvas types can it build?
Presentations, flowcharts, mood boards, knowledge graphs, galleries, storyboards, timelines, and dashboards, from 12 template archetypes.
How does it arrange nodes?
It uses one of 6 layout algorithms (auto/grid/dagre/radial/force/linear) with a 20px-grid auto-positioning algorithm.