Based on JSON Canvas Spec 1.0 Create and edit .canvas files.
File Structure
The Canvas file contains two top-level arrays:
{
"nodes": [],
"edges": []
}
nodes(optional): An array of nodes on the canvas, arranged in ascending z-index order (the first element is at the bottom layer)edges(optional): An array of edges connecting nodes
Nodes
All nodes share the following common properties:
| Property | Required | Type | Description |
|---|---|---|---|
id |
✅ | string | Unique identifier (16-digit lowercase hexadecimal) |
type |
✅ | string | text / file / link / group |
x |
✅ | integer | X coordinate (pixels, positive to the right) |
y |
✅ | integer | Y coordinate (pixels, positive downward) |
width |
✅ | integer | Width (pixels) |
height |
✅ | integer | Height (pixels) |
color |
❌ | canvasColor | Color (see the Color section) |
Text node (text)
Stores Markdown text content.
| Property | Required | Type | Description |
|---|---|---|---|
text |
✅ | string | Plain text in Markdown format |
{
"id": "6f0ad84f44ce9c17",
"type": "text",
"x": 0,
"y": 0,
"width": 400,
"height": 200,
"text": "# Hello World\n\nThis is **Markdown** content."
}
File node (file)
References a file or attachment (image, video, PDF, note, etc.).
| Property | Required | Type | Description |
|---|---|---|---|
file |
✅ | string | File path within the system |
subpath |
❌ | string | Subpath heading or block reference (starting with # ) |
{
"id": "a1b2c3d4e5f67890",
"type": "file",
"x": 500,
"y": 0,
"width": 400,
"height": 300,
"file": "Attachments/diagram.png"
}
Link node (link)
Displays an external URL.
| Property | Required | Type | Description |
|---|---|---|---|
url |
✅ | string | External URL |
{
"id": "c3d4e5f678901234",
"type": "link",
"x": 1000,
"y": 0,
"width": 400,
"height": 200,
"url": "https://obsidian.md"
}
Group node (group)
A visual container used to organize other nodes.
| Property | Required | Type | Description |
|---|---|---|---|
label |
❌ | string | Group label |
background |
❌ | string | Background image path |
backgroundStyle |
❌ | string | cover / ratio / repeat |
{
"id": "d4e5f6789012345a",
"type": "group",
"x": -50,
"y": -50,
"width": 1000,
"height": 600,
"label": "Project Overview",
"color": "4"
}
backgroundStyle values:
| Value | Description |
|---|---|
cover |
Fill the entire node |
ratio |
Maintain the image's original aspect ratio |
repeat |
Tile in the x/y direction |
Edges
Line segments connecting nodes.
| Property | Required | Type | Default Value | Description |
|---|---|---|---|---|
id |
✅ | string | — | Unique identifier |
fromNode |
✅ | string | — | Start node ID |
fromSide |
❌ | string | — | Start edge:top / right / bottom / left |
fromEnd |
❌ | string | none |
Start end shape:none / arrow |
toNode |
✅ | string | — | Target node ID |
toSide |
❌ | string | — | Target edge:top / right / bottom / left |
toEnd |
❌ | string | arrow |
End end shape:none / arrow |
color |
❌ | canvasColor | — | Line color |
label |
❌ | string | — | Text label |
{
"id": "f67890123456789a",
"fromNode": "6f0ad84f44ce9c17",
"fromSide": "right",
"toNode": "a1b2c3d4e5f67890",
"toSide": "left",
"toEnd": "arrow",
"label": "leads to"
}
Color
canvasColor The type supports two formats:
HEX color
{ "color": "#FF0000" }
Preset colors
| Preset Value | Color |
|---|---|
"1" |
🔴 Red |
"2" |
🟠 Orange |
"3" |
🟡 Yellow |
"4" |
🟢 Green |
"5" |
🔵 Cyan |
"6" |
🟣 Purple |
The specific color values of preset colors are defined by the application itself to match the brand colors.
ID Generation Rules
All id must be unique; it is recommended to use 16-character lowercase hexadecimal strings:
"id": "6f0ad84f44ce9c17"
Generation method: hexadecimal encoding of a 64-bit random value.
Layout Guidelines
Coordinate System
- Coordinates can be negative (the canvas extends infinitely)
xincreases to the right,yincreases downward- Coordinates point to the node'stop-left corner
Recommended Sizes
| Node Type | Recommended Width | Recommended Height |
|---|---|---|
| Small Text | 200–300 | 80–150 |
| Medium Text | 300–450 | 150–300 |
| Large Text | 400–600 | 300–500 |
| File Preview | 300–500 | 200–400 |
| Link Preview | 250–400 | 100–200 |
| Group | Depends on content | Depends on content |
Spacing
- Group padding: 20–50px
- Node spacing: 50–100px
- Align to a 10px or 20px grid
Validation Rules
- All
idare globally unique across nodes and edges fromNode/toNodemust reference an existing node ID- The required fields for each node type must be complete
typecan only betext/file/link/groupbackgroundStylecan only becover/ratio/repeatfromSide/toSidecan only betop/right/bottom/leftfromEnd/toEndcan only benone/arrow- The color must be
"1"–"6"or a valid HEX color (e.g.,#FF0000)
Complete Examples
Mind Map
{
"nodes": [
{
"id": "8a9b0c1d2e3f4a5b",
"type": "text",
"x": 0,
"y": 0,
"width": 300,
"height": 150,
"text": "# Main Idea\n\nThis is the central concept."
},
{
"id": "1a2b3c4d5e6f7a8b",
"type": "text",
"x": 400,
"y": -100,
"width": 250,
"height": 100,
"text": "## Supporting Point A\n\nDetails here."
},
{
"id": "2b3c4d5e6f7a8b9c",
"type": "text",
"x": 400,
"y": 100,
"width": 250,
"height": 100,
"text": "## Supporting Point B\n\nMore details."
}
],
"edges": [
{
"id": "3c4d5e6f7a8b9c0d",
"fromNode": "8a9b0c1d2e3f4a5b",
"fromSide": "right",
"toNode": "1a2b3c4d5e6f7a8b",
"toSide": "left"
},
{
"id": "4d5e6f7a8b9c0d1e",
"fromNode": "8a9b0c1d2e3f4a5b",
"fromSide": "right",
"toNode": "2b3c4d5e6f7a8b9c",
"toSide": "left"
}
]
}
Kanban
{
"nodes": [
{
"id": "5e6f7a8b9c0d1e2f",
"type": "group",
"x": 0,
"y": 0,
"width": 300,
"height": 500,
"label": "To Do",
"color": "1"
},
{
"id": "6f7a8b9c0d1e2f3a",
"type": "group",
"x": 350,
"y": 0,
"width": 300,
"height": 500,
"label": "In Progress",
"color": "3"
},
{
"id": "7a8b9c0d1e2f3a4b",
"type": "group",
"x": 700,
"y": 0,
"width": 300,
"height": 500,
"label": "Done",
"color": "4"
},
{
"id": "8b9c0d1e2f3a4b5c",
"type": "text",
"x": 20,
"y": 50,
"width": 260,
"height": 80,
"text": "## Task 1\n\nImplement feature X"
},
{
"id": "9c0d1e2f3a4b5c6d",
"type": "text",
"x": 370,
"y": 50,
"width": 260,
"height": 80,
"text": "## Task 2\n\nReview PR #123",
"color": "2"
},
{
"id": "0d1e2f3a4b5c6d7e",
"type": "text",
"x": 720,
"y": 50,
"width": 260,
"height": 80,
"text": "## Task 3\n\n~~Setup CI/CD~~"
}
],
"edges": []
}
Flowchart
{
"nodes": [
{
"id": "a0b1c2d3e4f5a6b7",
"type": "text",
"x": 200,
"y": 0,
"width": 150,
"height": 60,
"text": "**Start**",
"color": "4"
},
{
"id": "b1c2d3e4f5a6b7c8",
"type": "text",
"x": 200,
"y": 100,
"width": 150,
"height": 60,
"text": "Step 1:\nGather data"
},
{
"id": "c2d3e4f5a6b7c8d9",
"type": "text",
"x": 200,
"y": 200,
"width": 150,
"height": 80,
"text": "**Decision**\n\nIs data valid?",
"color": "3"
},
{
"id": "d3e4f5a6b7c8d9e0",
"type": "text",
"x": 400,
"y": 200,
"width": 150,
"height": 60,
"text": "Process data"
},
{
"id": "e4f5a6b7c8d9e0f1",
"type": "text",
"x": 0,
"y": 200,
"width": 150,
"height": 60,
"text": "Request new data",
"color": "1"
},
{
"id": "f5a6b7c8d9e0f1a2",
"type": "text",
"x": 400,
"y": 320,
"width": 150,
"height": 60,
"text": "**End**",
"color": "4"
}
],
"edges": [
{
"id": "a6b7c8d9e0f1a2b3",
"fromNode": "a0b1c2d3e4f5a6b7",
"fromSide": "bottom",
"toNode": "b1c2d3e4f5a6b7c8",
"toSide": "top"
},
{
"id": "b7c8d9e0f1a2b3c4",
"fromNode": "b1c2d3e4f5a6b7c8",
"fromSide": "bottom",
"toNode": "c2d3e4f5a6b7c8d9",
"toSide": "top"
},
{
"id": "c8d9e0f1a2b3c4d5",
"fromNode": "c2d3e4f5a6b7c8d9",
"fromSide": "right",
"toNode": "d3e4f5a6b7c8d9e0",
"toSide": "left",
"label": "Yes",
"color": "4"
},
{
"id": "d9e0f1a2b3c4d5e6",
"fromNode": "c2d3e4f5a6b7c8d9",
"fromSide": "left",
"toNode": "e4f5a6b7c8d9e0f1",
"toSide": "right",
"label": "No",
"color": "1"
},
{
"id": "e0f1a2b3c4d5e6f7",
"fromNode": "e4f5a6b7c8d9e0f1",
"fromSide": "top",
"fromEnd": "none",
"toNode": "b1c2d3e4f5a6b7c8",
"toSide": "left",
"toEnd": "arrow"
},
{
"id": "f1a2b3c4d5e6f7a8",
"fromNode": "d3e4f5a6b7c8d9e0",
"fromSide": "bottom",
"toNode": "f5a6b7c8d9e0f1a2",
"toSide": "top"
}
]
}
Research Canvas (with file and link nodes)
{
"nodes": [
{
"id": "1e2f3a4b5c6d7e8f",
"type": "text",
"x": 300,
"y": 200,
"width": 400,
"height": 200,
"text": "# Research Topic\n\n## Key Questions\n\n- How does X affect Y?\n- What are the implications?",
"color": "5"
},
{
"id": "2f3a4b5c6d7e8f9a",
"type": "file",
"x": 0,
"y": 0,
"width": 250,
"height": 150,
"file": "Literature/Paper A.pdf"
},
{
"id": "3a4b5c6d7e8f9a0b",
"type": "file",
"x": 0,
"y": 200,
"width": 250,
"height": 150,
"file": "Notes/Meeting Notes.md",
"subpath": "#Key Insights"
},
{
"id": "4b5c6d7e8f9a0b1c",
"type": "link",
"x": 0,
"y": 400,
"width": 250,
"height": 100,
"url": "https://example.com/research"
},
{
"id": "5c6d7e8f9a0b1c2d",
"type": "file",
"x": 750,
"y": 150,
"width": 300,
"height": 250,
"file": "Attachments/diagram.png"
}
],
"edges": [
{
"id": "6d7e8f9a0b1c2d3e",
"fromNode": "2f3a4b5c6d7e8f9a",
"fromSide": "right",
"toNode": "1e2f3a4b5c6d7e8f",
"toSide": "left",
"label": "supports"
},
{
"id": "7e8f9a0b1c2d3e4f",
"fromNode": "3a4b5c6d7e8f9a0b",
"fromSide": "right",
"toNode": "1e2f3a4b5c6d7e8f",
"toSide": "left",
"label": "informs"
},
{
"id": "8f9a0b1c2d3e4f5a",
"fromNode": "4b5c6d7e8f9a0b1c",
"fromSide": "right",
"toNode": "1e2f3a4b5c6d7e8f",
"toSide": "left",
"toEnd": "arrow",
"color": "6"
},
{
"id": "9a0b1c2d3e4f5a6b",
"fromNode": "1e2f3a4b5c6d7e8f",
"fromSide": "right",
"toNode": "5c6d7e8f9a0b1c2d",
"toSide": "left",
"label": "visualized by"
}
]
}
Utility Scripts
Validate Canvas File
node scripts/validate.js <file.canvas>
Generate Canvas Template
# 思维导图
node scripts/generate.js --template mindmap --title "My Topic" --items "Idea A,Idea B,Idea C" -o output.canvas
# 看板
node scripts/generate.js --template kanban --title "Sprint 1" --items "Task A,Task B,Task C" -o board.canvas
# 流程图
node scripts/generate.js --template flowchart --title "Process" --items "Start,Step 1,Decision,End" -o flow.canvas