BundledCreativeVersion 1.0.1

Creating Excalidraw Diagrams with Hermes Agent: JSON Format & Workflow

Hand-drawn Excalidraw JSON diagrams (arch, flow, seq).

Written by Neura Market from the official Hermes Agent documentation for Excalidraw. Commands, paths, and version numbers are reproduced from the source unchanged.

Read the official documentation

Excalidraw Diagram Reference

This reference describes how to create diagrams by writing standard Excalidraw element JSON and saving them as .excalidraw files. These files can be opened on excalidraw.com without accounts or API keys.

When to Use

Use this tool for:

  • Architecture diagrams
  • Flowcharts
  • Sequence diagrams
  • Concept maps
  • Any diagram that needs to be shared via a link or viewed/edited on excalidraw.com

Capabilities

  • Generate .excalidraw files from JSON element arrays
  • Drag-and-drop files onto excalidraw.com for viewing and editing
  • Upload files to excalidraw.com for shareable links (no account needed)
  • Create rectangles, ellipses, diamonds, arrows, and text elements
  • Bind text to shapes and arrows as labels
  • Connect arrows to shapes with fixed attachment points
  • Control drawing order (z-order) via array order
  • Set colors, fill styles, stroke styles, roundness, and arrowheads

Prerequisites

  • The Excalidraw skill must be loaded.
  • For uploading: the cryptography pip package must be installed. Run pip install cryptography in your terminal.

File Structure

Every .excalidraw file must use the following envelope structure. Replace ...your elements array here... with your actual array of element objects.

{
  "type": "excalidraw",
  "version": 2,
  "source": "hermes-agent",
  "elements": [ ...your elements array here... ],
  "appState": {
    "viewBackgroundColor": "#ffffff"
  }
}

The canvas background is white by default.

Procedures

Create and Save a Diagram

  1. Load the Excalidraw skill (already done).
  2. Write an array of Excalidraw element objects.
  3. Wrap the array in the standard .excalidraw envelope shown above.
  4. Save the file using write_file to a .excalidraw path (e.g., ~/diagrams/my_diagram.excalidraw).

Upload for a Shareable Link

  1. Ensure the cryptography pip package is installed (pip install cryptography).
  2. Run the upload script via terminal:
    python skills/creative/excalidraw/scripts/upload.py ~/diagrams/my_diagram.excalidraw
    
  3. The script prints a shareable URL.

Create a Labeled Shape (Container Binding)

  1. Create the shape element with a boundElements array containing an object with id (unique for the text) and type: "text".
  2. Create a text element with containerId set to the shape's id, plus fields: id, x, y, width, height, text, fontSize, fontFamily (1), strokeColor, textAlign ("center"), verticalAlign ("middle"), originalText (same as text), autoResize (true).
  3. Place the text element immediately after its container shape in the array.
  4. Do NOT use a "label" property on shapes.

Example of a rectangle with rounded corners, blue fill, and bound text "Hello":

{ "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 }

Create a Labeled Arrow

  1. Create the arrow element with boundElements containing an object with id and type: "text".
  2. Create a text element with containerId set to the arrow's id, plus fields as for shape labels.
  3. Place the text element immediately after the arrow in the array.

Example of an arrow labeled "connects":

{ "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 }

Connect an Arrow to Shapes

  1. Add startBinding and/or endBinding objects to the arrow element.
  2. Each binding has elementId (the shape's id) and fixedPoint (array [x, y] where 0-1: top=[0.5,0], bottom=[0.5,1], left=[0,0.5], right=[1,0.5]).

Example of an arrow connected to shapes:

{
  "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] }
}

Element Parameters

Common Parameters (All Elements)

ParameterMeaningRequired
typeElement type: rectangle, ellipse, diamond, arrow, or textYes
idUnique string identifier for the elementYes
xX coordinate of the element's left edge (for text, left edge; for shapes, top-left corner)Yes
yY coordinate of the element's top edgeYes
widthWidth of the elementYes
heightHeight of the elementYes
strokeColorColor of the stroke/border (default "#1e1e1e")No
backgroundColorFill color (default "transparent")No
fillStyleFill style: "solid", etc. (default "solid")No
strokeWidthStroke width (default 2)No
roughnessHand-drawn look level (default 1)No
opacityOpacity percentage (default 100)No

Shape-Specific Parameters

ParameterMeaningRequired
roundnessFor rectangles: roundness: { "type": 3 } for rounded cornersNo
boundElementsArray of objects with id and type ("text") to bind a text label to a shape or arrowFor labeled shapes/arrows

Text Element Parameters

ParameterMeaningRequired
containerIdOn a text element: the id of the shape or arrow it labelsFor bound text
textThe text content (for text elements)For text elements
fontSizeFont size in pixels (minimum 14; 16+ for body, 20+ for titles)For text elements
fontFamilyFont family (fontFamily: 1 = Virgil/hand-drawn)For text elements (always 1)
textAlignHorizontal alignment (e.g., "center")For bound text
verticalAlignVertical alignment (e.g., "middle")For bound text
originalTextShould match text fieldFor text elements
autoResizeBoolean (true recommended for text)For text elements

Arrow Parameters

ParameterMeaningRequired
pointsArray of [dx, dy] offsets from element x,y defining the arrow pathFor arrows
endArrowheadArrowhead style: null, "arrow", "bar", "dot", "triangle"No
strokeStyleLine style: "solid" (default), "dashed", "dotted"No
startBindingObject with elementId and fixedPoint to attach arrow start to a shapeNo
endBindingObject with elementId and fixedPoint to attach arrow end to a shapeNo

Element Examples

Rectangle

{ "type": "rectangle", "id": "r1", "x": 100, "y": 100, "width": 200, "height": 100 }

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 }

Arrow (Unlabeled)

{ "type": "arrow", "id": "a1", "x": 300, "y": 150, "width": 200, "height": 0,
  "points": [[0,0],[200,0]], "endArrowhead": "arrow" }

Standalone Text

{ "type": "text", "id": "t1", "x": 150, "y": 138, "text": "Hello", "fontSize": 20,
  "fontFamily": 1, "strokeColor": "#1e1e1e", "originalText": "Hello", "autoResize": true }

For standalone text positioning, do not rely on textAlign or width for positioning. Use x = cx - (text.length * fontSize * 0.5) / 2.

Constraints and Caveats

  • Do NOT use "label": { "text": "..." } on shapes; it is silently ignored and results in blank shapes.
  • Text x/y/width/height for bound text are approximate; Excalidraw recalculates on load.
  • originalText must match text exactly.
  • fontFamily must be 1 (Virgil/hand-drawn).
  • Do NOT use emoji in text; they do not render.
  • Minimum fontSize: 14 (sparingly), 16 for body/labels, 20 for titles.
  • Minimum shape size: 120x60 for labeled rectangles/ellipses.
  • Leave 20-30px gaps between elements.
  • Prefer fewer, larger elements over many tiny ones.
  • Text contrast: never use light gray on white; minimum text color on white is #757575.
  • Array order determines z-order: first = back, last = front.
  • Place bound text immediately after its container shape/arrow in the array.
  • Emit elements progressively: background zones, shape, its text, its arrows, next shape.
  • The upload script requires the cryptography pip package.
  • Canvas background is white by default.

Color Palette

Refer to references/colors.md for a full palette. Common colors include:

  • #a5d8ff (Light Blue - primary/input)
  • #b2f2bb (Light Green - success/output)
  • #ffd8a8 (Light Orange - warning/external)
  • #d0bfff (Light Purple - processing/special)
  • #ffc9c9 (Light Red - error/critical)
  • #fff3bf (Light Yellow - notes/decisions)
  • #c3fae8 (Light Teal - storage/data)

For dark mode considerations, see references/dark-mode.md. For more examples, see references/examples.md.

Failure Modes

  • Using "label" property on shapes results in blank shapes (silently ignored).
  • Emoji in text does not render.
  • Low contrast text (e.g., light gray on white) is unreadable.
  • Incorrect z-order (all rectangles first, then all texts, etc.) causes overlapping issues.
  • Missing boundElements or containerId breaks label binding.
  • fontFamily not set to 1 may cause font rendering issues.
  • fontSize below 14 may be unreadable.
  • Arrow points not matching element position may misplace the arrow.
  • Upload fails if cryptography is not installed.

More Creative skills