From claude-obsidian
Manages Obsidian canvas files as visual reference boards: creates canvases, adds images/text/PDFs/wiki pages with auto-positioning into zones, lists contents, and integrates with image capture.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-obsidian:canvasThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
The three knowledge capture layers:
The three knowledge capture layers:
/save → text synthesis (wiki/questions/, wiki/concepts/)/autoresearch → structured knowledge (wiki/sources/, wiki/concepts/)/canvas → visual references (wiki/canvases/)A canvas is a JSON file Obsidian renders as an infinite visual board. This skill reads and writes canvas JSON directly. Read references/canvas-spec.md for the full format reference before making any edits. This spec aligns with the JSON Canvas open standard.
Substrate preference (v1.7+): This skill is a self-contained fallback. Prefer kepano/obsidian-skills as the authoritative substrate — its json-canvas skill is the canonical spec reference. If you see a json-canvas skill available without the claude-obsidian: namespace, that is kepano's version: use it for spec questions. Continue to use this canvas skill for the wiki-scoped workflows (positioning into wiki/canvases/, /banana integration, zone layout) — those are unique to claude-obsidian and live above kepano's primitive. Install kepano: claude plugin marketplace add kepano/obsidian-skills.
wiki/canvases/main.canvas
If it does not exist, create it:
{
"nodes": [
{
"id": "title",
"type": "text",
"text": "# Visual Reference\n\nDrop images, PDFs, and notes here.",
"x": -400, "y": -300, "width": 400, "height": 120, "color": "6"
},
{
"id": "zone-default",
"type": "group",
"label": "General",
"x": -400, "y": -140, "width": 800, "height": 400, "color": "4"
}
],
"edges": []
}
/canvas with no args)wiki/canvases/main.canvas exists.wiki/canvases/main.canvas in Obsidian to view."/canvas new [name])wiki/canvases/[slug].canvas with the starter structure, title updated to # [Name].wiki/overview.md under a "## Canvases" subsection (append after the Current State section). Do not modify wiki/index.md. It uses a fixed section schema (Domains, Entities, Concepts, Sources, Questions, Comparisons)./canvas add image [path or url])Resolve the image:
http): download with curl -sL [url] -o _attachments/images/canvas/[filename]
Derive filename from URL path, or use img-[timestamp].jpg if unclear.cp [path] _attachments/images/canvas/Create _attachments/images/canvas/ if it doesn't exist.
Detect aspect ratio:
Use python3 -c "from PIL import Image; img=Image.open('[path]'); print(img.width, img.height)" or identify -format '%w %h' [path].
See references/canvas-spec.md for the full aspect ratio → canvas size table (7 ratios including 4:3, 3:4, ultra-wide). Do not use an inline table here. The spec is the single source of truth for sizing.
Position using auto-layout (see Auto-Positioning section below).
Append node to canvas JSON and write.
Report: "Added [filename] to [zone] zone at position ([x], [y])."
/canvas add text [content])Create a text node:
{
"id": "text-[timestamp]",
"type": "text",
"text": "[content]",
"x": [auto], "y": [auto],
"width": 300, "height": 120,
"color": "4"
}
Position using auto-layout. Write and report.
/canvas add pdf [path])Same as add image. Obsidian renders PDFs natively as file nodes.
_attachments/pdfs/canvas/ if outside vault./canvas add note [wiki-page])wiki/ for a file matching the page name (case-insensitive, partial match ok).file field.
"type": "file" (not "type": "link"): .md files use file nodes, not link nodes."type": "link" takes a url: "https://...": it is for web URLs only.{
"id": "note-[timestamp]",
"type": "file",
"file": "wiki/concepts/LLM Wiki Pattern.md",
"x": [auto], "y": [auto],
"width": 300, "height": 100
}
/canvas zone [name] [color])max(node.y + node.height for all nodes) + 60. Use 280 if no nodes (leaves room above the starter title node).{
"id": "zone-[slug]",
"type": "group",
"label": "[name]",
"x": -400,
"y": [max_y],
"width": 1000,
"height": 400,
"color": "[color or '3']"
}
Valid colors: "1"=red "2"=orange "3"=yellow "4"=green "5"=cyan "6"=purple
Write and report.
/canvas list)glob wiki/canvases/*.canvaswiki/canvases/main.canvas . 14 nodes (8 images, 3 text, 2 file, 1 group)
wiki/canvases/design-ideas.canvas. 42 nodes (30 images, 4 text, 8 groups)
/canvas from banana) (if the banana-claude plugin is installed)wiki/canvases/.recent-images.txt first (session log of newly written images).find with correct precedence (parentheses required. Without them -newer only binds to the last -name clause):
python3 -c "import time,os; open('/tmp/ten-min-ago','w').close(); os.utime('/tmp/ten-min-ago',(time.time()-600,time.time()-600))"
find _attachments/images -newer /tmp/ten-min-ago \( -name "*.png" -o -name "*.jpg" \)
Note: /banana is an optional external skill not shipped in this plugin. If the user has it installed, the .recent-images.txt log will be populated. If not, the find command above is the fallback.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 -400, max_y + 60
zx, zy = zone['x'], zone['y']
zw, zh = zone['width'], zone['height']
# Nodes inside this zone
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 zx + 20, zy + 20
rightmost_x = max(n['x'] + n.get('width', 0) for n in inside)
next_x = rightmost_x + 40
if next_x + new_w > zx + zw:
# New row
max_row_y = max(n['y'] + n.get('height', 0) for n in inside)
return zx + 20, max_row_y + 20
# Same row: align to the top of all existing nodes in the zone
current_row_y = min(n['y'] for n in inside)
return next_x, current_row_y
Read the canvas, collect all existing IDs. Never reuse one.
Safe ID pattern: [type]-[content-slug]-[full-unix-timestamp]
Use the full Unix timestamp (10 digits) to avoid collisions in batch operations.
Examples: img-cover-1744032823, text-note-1744032845, zone-branding-1744032901
If a collision is detected (ID already exists in the canvas), append -2, -3, etc.
If wiki/canvases/.recent-images.txt exists, append any new image path written to _attachments/images/ during this session (one path per line, keep last 20).
/canvas from banana reads this file first, making it instant without filesystem search.
After any /banana run in the same session, if the user says "add to canvas" or "put on canvas", treat it as /canvas from banana.
When /banana finishes generating images, suggest:
"Add generated images to canvas? Run
/canvas from banana"
_attachments/images/canvas/ for downloaded/copied images.wiki/index.md when creating new canvases.For standalone visual production (12 templates, 6 layout algorithms, AI generation, presentations), see claude-canvas. This skill handles wiki-scoped visual boards. claude-canvas handles full-featured canvas orchestration for any project.
When working on this skill, apply the 10-principle loop. See skills/think/SKILL.md for the canonical framework.
| # | Principle | Application here |
|---|---|---|
| 1 | OBSERVE (ext) | Which images, PDFs, notes belong on this canvas? Read each before adding. |
| 2 | OBSERVE (int) | Am I aestheticizing or actually communicating? Pretty canvases that don't inform are noise. |
| 3 | LISTEN | The user's mental model of how these items relate. The canvas should mirror that, not impose another. |
| 4 | THINK | Layout, group hierarchy, edge structure. Spatial reasoning matters; arbitrary positions confuse. |
| 5 | CONNECT (lat) | Edges between canvas nodes reveal hidden structure not visible in the linear wiki. |
| 6 | CONNECT (sys) | JSON Canvas 1.0 spec + Obsidian-native rendering + banana skill for AI image gen. |
| 7 | FEEL | A canvas should be readable at first glance, not a maze of arrows. |
| 8 | ACCEPT | Not every project needs a canvas. Admit when prose is enough. |
| 9 | CREATE | Write the .canvas JSON with stable IDs and sensible positions. |
| 10 | GROW | Which canvases get reopened? Which are abandoned? That signal informs canvas-worthiness over time. |
npx claudepluginhub agricidaniel/claude-obsidian --plugin claude-obsidianAI-orchestrated visual production for Obsidian Canvas. Create presentations, flowcharts, mood boards, knowledge graphs, galleries, storyboards, timelines, dashboards, and more with intelligent layout and AI-generated content. Claude acts as Creative Director — dispatching sub-agents for image generation, SVG diagrams, GIF creation, and spatial layout. Supports 12 template archetypes, 6 layout algorithms, and Advanced Canvas presentation mode. Triggers on: /canvas, create canvas, build canvas, make a presentation, visual board, mood board, flowchart canvas, storyboard, canvas from template, lay out canvas, export canvas, canvas layout, canvas generate, add to canvas, put this on the canvas, open canvas, canvas present, canvas template.
Creates and edits Obsidian JSON Canvas (.canvas) files with nodes, edges, groups for mind maps, flowcharts, and infinite visual diagrams.
Creates and edits Obsidian JSON Canvas (.canvas) files with nodes, edges, groups, and connections for mind maps, flowcharts, and visual diagrams.