Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Hand-drawn Excalidraw JSON diagrams (arch, flow, seq).
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | 96% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 207% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 424% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 166% | 0% |
Create diagrams by writing standard Excalidraw element JSON and saving as .excalidraw files. These files can be drag-and-dropped onto excalidraw.com for viewing and editing. No accounts, no API keys, no rendering libraries -- just JSON.
Generate .excalidraw files for architecture diagrams, flowcharts, sequence diagrams, concept maps, and more. Files can be opened at excalidraw.com or uploaded for shareable links.
write_file to create a .excalidraw filescripts/upload.py via terminalWrap your elements array in the standard .excalidraw envelope and save with write_file:
json{ "type": "excalidraw", "version": 2, "source": "hermes-agent", "elements": [ ...your elements array here... ], "appState": { "viewBackgroundColor": "#ffffff" } }
Save to any path, e.g. ~/diagrams/my_diagram.excalidraw.
Run the upload script (located in this skill's scripts/ directory) via terminal:
bashpython skills/creative/excalidraw/scripts/upload.py ~/diagrams/my_diagram.excalidraw
This uploads to excalidraw.com (no account needed) and prints a shareable URL. Requires the cryptography pip package (pip install cryptography).
type, id (unique string), x, y, width, height
strokeColor: "#1e1e1e"backgroundColor: "transparent"fillStyle: "solid"strokeWidth: 2roughness: 1 (hand-drawn look)opacity: 100Canvas background is white.
Rectangle:
json{ "type": "rectangle", "id": "r1", "x": 100, "y": 100, "width": 200, "height": 100 }
roundness: { "type": 3 } for rounded cornersbackgroundColor: "#a5d8ff", fillStyle: "solid" for filledEllipse:
json{ "type": "ellipse", "id": "e1", "x": 100, "y": 100, "width": 150, "height": 150 }
Diamond:
json{ "type": "diamond", "id": "d1", "x": 100, "y": 100, "width": 150, "height": 150 }
Labeled shape (container binding) -- create a text element bound to the shape:
> WARNING: Do NOT use "label": { "text": "..." } on shapes. This is NOT a valid > Excalidraw property and will be silently ignored, producing blank shapes. You MUST > use the container binding approach below.
The shape needs boundElements listing the text, and the text needs containerId pointing back:
json{ "type": "rectangle", "id": "r1", "x": 100, "y": 100, "width": 200, "height": 80, "roundness": { "type": 3 }, "backgroundColor": "#a5d8ff", "fillStyle": "solid", "boundElements": [{ "id": "t_r1", "type": "text" }] }, { "type": "text", "id": "t_r1", "x": 105, "y": 110, "width": 190, "height": 25, "text": "Hello", "fontSize": 20, "fontFamily": 1, "strokeColor": "#1e1e1e", "textAlign": "center", "verticalAlign": "middle", "containerId": "r1", "originalText": "Hello", "autoResize": true }
containerId is setx/y/width/height are approximate -- Excalidraw recalculates them on loadoriginalText should match textfontFamily: 1 (Virgil/hand-drawn font)Labeled arrow -- same container binding approach:
json{ "type": "arrow", "id": "a1", "x": 300, "y": 150, "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": 130, "width": 60, "height": 20, "text": "connects", "fontSize": 16, "fontFamily": 1, "strokeColor": "#1e1e1e", "textAlign": "center", "verticalAlign": "middle", "containerId": "a1", "originalText": "connects", "autoResize": true }
Standalone text (titles and annotations only -- no container):
json{ "type": "text", "id": "t1", "x": 150, "y": 138, "text": "Hello", "fontSize": 20, "fontFamily": 1, "strokeColor": "#1e1e1e", "originalText": "Hello", "autoResize": true }
x is the LEFT edge. To center at position cx: x = cx - (text.length * fontSize * 0.5) / 2textAlign or width for positioningArrow:
json{ "type": "arrow", "id": "a1", "x": 300, "y": 150, "width": 200, "height": 0, "points": [[0,0],[200,0]], "endArrowhead": "arrow" }
points: [dx, dy] offsets from element x, yendArrowhead: null | "arrow" | "bar" | "dot" | "triangle"strokeStyle: "solid" (default) | "dashed" | "dotted"json{ "type": "arrow", "id": "a1", "x": 300, "y": 150, "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]
Font sizes:
fontSize: 16 for body text, labels, descriptionsfontSize: 20 for titles and headingsfontSize: 14 for secondary annotations only (sparingly)fontSize below 14Element sizes:
See references/colors.md for full color tables. Quick reference:
| 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 |
#757575references/dark-mode.mdreferences/examples.mdOther measured skills in the registry, with their headline benchmark lift.