/install hermes-excalidraw
Excalidraw Diagram Skill
Create .excalidraw files that can be drag-dropped onto excalidraw.com or shared via URL. No account, no API keys, just JSON.
When to use
Architecture diagrams, flowcharts, sequence diagrams, concept maps, process maps — any visual you want to communicate clearly.
Workflow
- Load this skill (you already did)
- Design the layout (read the Layout Rules section first — this is where most bad diagrams fail)
- Write the elements JSON — follow the Layout Rules strictly
- Pre-flight check — verify all box widths and arrow lengths against Rule 6 before saving
- Save as
.excalidrawviawrite_file - Upload for a shareable link via
scripts/upload.pyinterminal - Verify with screenshot — check for overflow issues, fix and re-upload if needed
Layout Rules (MOST IMPORTANT SECTION)
Bad diagrams fail for 4 reasons: inconsistent box sizes, meaningless colors, crossing lines, disorganized arrangement. Fix all four with these rules:
Rule 1 — Use a Grid
Never place elements ad-hoc. Design on a virtual grid:
- Column width: 200–240px per logical column
- Row height: 100–120px per logical row
- Gap between boxes: minimum 30px horizontal, 25px vertical
- X positions: 0, 240, 480, 720, 960 (every 240px)
- Y positions: 0, 120, 240, 360, 480, 600 (every 120px)
Rule 2 — Consistent Box Sizing
All boxes in the same logical row or category MUST have identical dimensions. Use one size for all level-1 nodes, another for all level-2 nodes. Only vary size for intentional hierarchy.
| Role | Width | Height |
|---|---|---|
| Level-1 node (main step) | 200 | 70 |
| Level-2 node (sub-step) | 180 | 60 |
| Title banner | 400 | 50 |
| Footer / badge | 160 | 40 |
Rule 3 — Meaningful Color Coding
Colors must communicate semantics, not decorate. Pick ONE role per diagram:
Role A — Flow / Process (arrows show direction):
| Meaning | Fill Color | Hex |
|---|---|---|
| Start / Input | Light Blue | #a5d8ff |
| Step / Action | Light Blue | #a5d8ff |
| Decision | Light Yellow | #fff3bf |
| Success / Output | Light Green | #b2f2bb |
| External / Pending | Light Orange | #ffd8a8 |
| Error / Alert | Light Red | #ffc9c9 |
Role B — Layered Architecture (zones show grouping):
| Meaning | Fill Color | Hex |
|---|---|---|
| UI / Frontend layer | Blue zone | #dbe4ff |
| Logic / Agent layer | Purple zone | #e5dbff |
| Data / Tool layer | Green zone | #d3f9d8 |
Role C — Comparison (side-by-side):
| Meaning | Fill Color | Hex |
|---|---|---|
| This side | Light Blue | #a5d8ff |
| That side | Light Purple | #d0bfff |
Pick ONE role per diagram. Do NOT mix Role A and Role B colors in the same diagram.
Rule 4 — No Crossing Lines
Arrows must never cross other content. Techniques:
- Sequential flow (left→right): all arrows point in one direction, use vertical spacing to separate rows
- Vertical stacking: if 3+ arrows would cross, stack them vertically with 15px gaps
- L-shapes: use
points: [[0,0],[dx,0],[dx,dy]]to route around obstacles - Start/end bindings: use
startBinding/endBindingwithfixedPointso arrows snap to shape edges cleanly
Rule 5 — Meaningful Arrangement
Layout must match the conceptual model:
- Flowchart: strict left→right or top→bottom. No boxes above arrows.
- Layers: background zones drawn FIRST (behind everything), then nodes inside each zone
- Comparison: left vs right, never diagonal
- Title at top, footer/result at bottom
Rule 6 — Content Must Fit Inside Boxes
This is the #1 reason diagrams look broken after upload. Text that overflows its container makes the diagram unreadable. Prevent it with these rules:
Minimum box widths by text length (fontSize 16, Virgel font):
| Characters in label | Minimum box width |
|---|---|
| 1–8 chars | 120 px |
| 9–14 chars | 160 px |
| 15–20 chars | 200 px |
| 21–28 chars | 240 px |
| 29+ chars | split into two lines or reduce fontSize |
Width formula: box_width >= text_length * 9.5 (at fontSize 16, Virgel = ~9.5px per character)
Arrow label width: The arrow must be at least label_chars * 9.5 + 40 px long to fit the label clearly. If the flow is too short, stack arrows vertically rather than making them short.
Text length hard limits:
- Box labels: max 28 characters per line. Use
line1\ line2for longer text (max 2 lines). - Arrow labels: max 30 characters. Keep them short.
- Standalone text: no hard limit but prefer brevity.
What to check before saving:
- Count the longest label in each row
- Verify the box width >= that count × 9.5
- Verify arrow length >= its label length × 9.5 + 40
- If any box has width \x3C 120px, widen it — no exceptions
Warning signs of overflow:
- Text looks squished or overlaps box edges → box too narrow
- Arrow label clips into the arrow line or bleeds outside → arrow too short
- Text trails off the right edge of a box → width is insufficient
- Multi-word label on one line but box is narrow → split with
\or widen box
Element Format Reference
Required Fields (all elements)
type, id (unique string), x, y, width, height
Defaults (applied automatically — skip these)
strokeColor:"#1e1e1e"backgroundColor:"transparent"fillStyle:"solid"strokeWidth:2roughness:1(hand-drawn look)opacity:100
Element Types
Rectangle:
{ "type": "rectangle", "id": "r1", "x": 100, "y": 100, "width": 200, "height": 70 }
roundness: { "type": 3 }for rounded cornersbackgroundColor: "#a5d8ff",fillStyle: "solid"for filled
Ellipse:
{ "type": "ellipse", "id": "e1", "x": 100, "y": 100, "width": 150, "height": 150 }
Diamond:
{ "type": "diamond", "id": "d1", "x": 100, "y": 100, "width": 150, "height": 150 }
Labeled shape (container binding) — do NOT use "label": { "text": "..." } on shapes:
{
"type": "rectangle", "id": "r1", "x": 100, "y": 100, "width": 200, "height": 70,
"roundness": { "type": 3 }, "backgroundColor": "#a5d8ff", "fillStyle": "solid",
"boundElements": [{ "id": "t_r1", "type": "text" }]
},
{
"type": "text", "id": "t_r1", "x": 105, "y": 118, "width": 190, "height": 25,
"text": "Start", "fontSize": 18, "fontFamily": 1, "strokeColor": "#1e1e1e",
"textAlign": "center", "verticalAlign": "middle",
"containerId": "r1", "originalText": "Start", "autoResize": true
}
- Works on rectangle, ellipse, diamond
- Text is auto-centered by Excalidraw when
containerIdis set - The text
x/y/width/heightare approximate — Excalidraw recalculates them on load originalTextmust matchtext- Always include
fontFamily: 1(Virgil/hand-drawn font)
Labeled arrow — same container binding approach:
{
"type": "arrow", "id": "a1", "x": 300, "y": 135, "width": 200, "height": 0,
"points": [[0,0],[200,0]], "endArrowhead": "arrow",
"boundElements": [{ "id": "t_a1", "type": "text" }]
},
{
"type": "text", "id": "t_a1", "x": 370, "y": 118, "width": 60, "height": 25,
"text": "goes to", "fontSize": 14, "fontFamily": 1, "strokeColor": "#1e1e1e",
"textAlign": "center", "verticalAlign": "middle",
"containerId": "a1", "originalText": "goes to", "autoResize": true
}
Standalone text (titles and annotations only — no container):
{
"type": "text", "id": "t1", "x": 150, "y": 138, "text": "Hello",
"fontSize": 24, "fontFamily": 1, "strokeColor": "#1e1e1e",
"originalText": "Hello", "autoResize": true
}
xis the LEFT edge- Do NOT rely on
textAlignorwidthfor positioning
Arrow:
{
"type": "arrow", "id": "a1", "x": 300, "y": 135, "width": 200, "height": 0,
"points": [[0,0],[200,0]], "endArrowhead": "arrow"
}
points:[dx, dy]offsets from elementx,yendArrowhead:null|"arrow"|"bar"|"dot"|"triangle"strokeStyle:"solid"(default) |"dashed"|"dotted"
Arrow Bindings
{
"type": "arrow", "id": "a1", "x": 300, "y": 135, "width": 150, "height": 0,
"points": [[0,0],[150,0]], "endArrowhead": "arrow",
"startBinding": { "elementId": "r1", "fixedPoint": [1, 0.5] },
"endBinding": { "elementId": "r2", "fixedPoint": [0, 0.5] }
}
fixedPoint coordinates: top=[0.5,0], bottom=[0.5,1], left=[0,0.5], right=[1,0.5]
Drawing Order (z-order)
Array order = z-order (first = back, last = front). Emit progressively: background zones → shape → its bound text → its arrows → next shape.
- BAD: all rectangles, then all texts, then all arrows
- GOOD: bg_zone → shape1 → text_for_shape1 → arrow1 → shape2 → text_for_shape2 → ...
- Always place the bound text element immediately after its container shape
Sizing Guidelines
Font sizes:
fontSize: 20for titles and headingsfontSize: 16for body text, labels, descriptionsfontSize: 14for secondary annotations only (sparingly)- NEVER use
fontSizebelow 14
Element sizes:
- Minimum shape size: 120x60 for labeled rectangles/ellipses
- Leave 20-30px gaps between elements minimum
- Prefer fewer, larger elements over many tiny ones
MANDATORY: Pre-check text fit before finalizing
- List every box's label text and count characters
- Apply the Rule 6 minimum-width table — if any box is narrower than required, widen it first
- For every arrow with a label, verify arrow width >=
label_chars × 9.5 + 40 - If text in a box feels even slightly cramped, add 20px of extra width
Color Palette
Pastel Fills (for shape backgrounds)
| Use | Fill Color | Hex |
|---|---|---|
| Primary / Input | Light Blue | #a5d8ff |
| Success / Output | Light Green | #b2f2bb |
| Warning / External | Light Orange | #ffd8a8 |
| Processing / Special | Light Purple | #d0bfff |
| Error / Critical | Light Red | #ffc9c9 |
| Notes / Decisions | Light Yellow | #fff3bf |
| Storage / Data | Light Teal | #c3fae8 |
| Analytics | Light Pink | #eebefa |
Background Zones (opacity 30-35 for layered diagrams)
| Use | Fill Color | Hex |
|---|---|---|
| UI / Frontend layer | Blue zone | #dbe4ff |
| Logic / Agent layer | Purple zone | #e5dbff |
| Data / Tool layer | Green zone | #d3f9d8 |
Text Contrast Rules
- On white backgrounds: minimum text color is
#757575. Default#1e1e1eis best. - Colored text on light fills: use dark variants (
#15803dnot#22c55e,#2563ebnot#4a9eed) - White text: only on dark backgrounds (
#9a5030not#c4795b) - Never: light gray (
#b0b0b0,#999) on white — unreadable
Saving & Uploading
Saving a Diagram
{
"type": "excalidraw",
"version": 2,
"source": "hermes-agent",
"elements": [ ...elements... ],
"appState": { "viewBackgroundColor": "#ffffff" }
}
Save with write_file to any path, e.g. ~/diagrams/my_diagram.excalidraw.
Uploading for a Shareable Link
python ~/.hermes/skills/creative/excalidraw/scripts/upload.py ~/diagrams/my_diagram.excalidraw
This encrypts client-side (AES-GCM), embeds the key in the URL fragment, and uploads to excalidraw.com. Each upload gets a unique URL — avoids the "file already exists" confirmation prompt. No account needed.
Requires: pip install cryptography
Common Mistakes to Avoid
-
TEXT OVERFLOWS THE BOX (the most common failure mode):
- Causes: box too narrow for its label, arrow too short for its label, multi-line text crammed into one line
- Symptom: text bleeds outside box edges, labels get clipped, arrows show "..." inside them
- Fix: always use the Rule 6 minimum-width table to pre-calculate box widths. Make arrows at least
label_chars × 9.5 + 40px long - Rule of thumb: if text feels "tight", the box is too small — add 40px of breathing room
-
Do NOT use
"label"on shapes — this is NOT a valid Excalidraw property. It will be silently ignored, producing blank shapes. Always use container binding (containerId+boundElements). -
Every bound text needs both sides linked — the shape needs
boundElements: [{"id": "t_xxx", "type": "text"}]AND the text needscontainerId: "shape_id". -
Elements overlap when y-coordinates are close — always check that text, boxes, and labels don't stack on top of each other.
-
Arrow labels overflow short arrows — keep labels short or make arrows wider.
-
Center titles relative to the diagram — estimate total width and center the title text over it.
-
Draw decorations LAST — sun, stars, icons should appear at the end of the array so they're on top.
-
Color without meaning — every color choice must map to a concept in the diagram. Random color assignment is noise.
Verify with Screenshot (Required Step)
After uploading, always verify the result with a real screenshot. This catches layout problems that JSON inspection can't.
Option A — PinchTab (preferred, uses your real Chrome)
Requires PinchTab Chrome extension installed:
# 1. Navigate to the Excalidraw URL
curl -X POST http://localhost:9867/navigate \
-H "Content-Type: application/json" \
-d '{"url": "https://excalidraw.com/#json=\x3CFILE_ID>,\x3CKEY>"}'
# 2. Wait for render
sleep 4
# 3. Screenshot
curl -X POST http://localhost:9867/screenshot \
-H "Content-Type: application/json" \
-d '{"format": "jpeg", "quality": 85}' \
--output ~/diagrams/my_diagram.jpg
Then load the screenshot with vision_analyze to verify layout quality.
Option B — Chrome CDP (manual Chrome required)
The user must first start Chrome manually with debug port:
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/chrome-screenshot &
Then use the screenshot script:
python ~/work/ai/omnimcp-opencli/scripts/chrome_screenshot.py \
"https://excalidraw.com/#json=\x3CFILE_ID>,\x3CKEY>" \
~/diagrams/my_diagram.jpg
What to Check on the Screenshot
- Box sizes — all boxes in same row same size? Text fits inside?
- Color coding — does color map to a consistent meaning across the whole diagram?
- Arrow crossings — no line crosses through any box or other line
- Content — all key information visible and readable
- Arrangement — does layout match the conceptual model (flow left→right, layers stacked)?
If problems found: patch the JSON and re-upload. Iterate until 8+/10.
Complete Examples
See references/examples.md for copy-pasteable complete diagrams covering:
- Two connected labeled boxes (minimal flowchart)
- Photosynthesis process diagram (multi-node with background zones)
- MCP sequence diagram (actors, dashed lifelines, message arrows)
See references/skill-selection-model.md for the 3-layer skill selection architecture (Hermes/OpenClaw model) — useful when designing skill catalogs or building workflow diagrams about agent behavior.
- 确保已安装 OpenClaw(本地或 Docker 部署)
- 在对话框中输入安装命令:
/install hermes-excalidraw - 安装完成后,直接呼叫该 Skill 的名称或使用
/hermes-excalidraw触发 - 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
Excalidraw 是什么?
Hand-drawn Excalidraw JSON diagrams — architecture, flow, sequence. Generate clean, well-laid-out charts with consistent box sizing, meaningful colors, and z... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 35 次。
如何安装 Excalidraw?
在 OpenClaw 或 Claude Code 对话框中运行命令「/install hermes-excalidraw」即可一键安装,无需额外配置。
Excalidraw 是免费的吗?
是的,Excalidraw 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。
Excalidraw 支持哪些平台?
Excalidraw 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。
谁开发了 Excalidraw?
由 Wade Deng(@no7dw)开发并维护,当前版本 v2.1.0。