Files
awesome-copilot/skills/excalidraw-diagram-generator/references/excalidraw-schema.md

8.2 KiB

Excalidraw JSON Schema Reference

This document describes the structure of Excalidraw .excalidraw files for diagram generation.

Top-Level Structure

interface ExcalidrawFile {
  type: "excalidraw";
  version: number;           // Always 2
  source: string;            // "https://excalidraw.com"
  elements: ExcalidrawElement[];
  appState: AppState;
  files: Record<string, any>; // Usually empty {}
}

AppState

interface AppState {
  viewBackgroundColor: string; // Hex color, e.g., "#ffffff"
  gridSize: number;            // Typically 20
}

ExcalidrawElement Base Properties

All elements share these common properties:

interface BaseElement {
  id: string;                  // Unique identifier
  type: ElementType;           // See Element Types below
  x: number;                   // X coordinate (pixels from top-left)
  y: number;                   // Y coordinate (pixels from top-left)
  width: number;               // Width in pixels
  height: number;              // Height in pixels
  angle: number;               // Rotation angle in radians (usually 0)
  strokeColor: string;         // Hex color, e.g., "#1e1e1e"
  backgroundColor: string;     // Hex color or "transparent"
  fillStyle: "solid" | "hachure" | "cross-hatch";
  strokeWidth: number;         // 1-4 typically
  strokeStyle: "solid" | "dashed" | "dotted";
  roughness: number;           // 0-2, controls hand-drawn effect (1 = default)
  opacity: number;             // 0-100
  groupIds: string[];          // IDs of groups this element belongs to
  frameId: null;               // Usually null
  index: string;               // Stacking order identifier
  roundness: Roundness | null;
  seed: number;                // Random seed for deterministic rendering
  version: number;             // Element version (increment on edit)
  versionNonce: number;        // Random number changed on edit
  isDeleted: boolean;          // Should be false
  boundElements: any;          // Usually null
  updated: number;             // Timestamp in milliseconds
  link: null;                  // External link (usually null)
  locked: boolean;             // Whether element is locked
}

Element Types

Rectangle

interface RectangleElement extends BaseElement {
  type: "rectangle";
  roundness: { type: 3 };      // 3 = rounded corners
  text?: string;               // Optional text inside
  fontSize?: number;           // Font size (16-32 typical)
  fontFamily?: number;         // 1 = Virgil, 2 = Helvetica, 3 = Cascadia
  textAlign?: "left" | "center" | "right";
  verticalAlign?: "top" | "middle" | "bottom";
}

Example:

{
  "id": "rect1",
  "type": "rectangle",
  "x": 100,
  "y": 100,
  "width": 200,
  "height": 100,
  "strokeColor": "#1e1e1e",
  "backgroundColor": "#a5d8ff",
  "text": "My Box",
  "fontSize": 20,
  "textAlign": "center",
  "verticalAlign": "middle",
  "roundness": { "type": 3 }
}

Ellipse

interface EllipseElement extends BaseElement {
  type: "ellipse";
  text?: string;
  fontSize?: number;
  fontFamily?: number;
  textAlign?: "left" | "center" | "right";
  verticalAlign?: "top" | "middle" | "bottom";
}

Diamond

interface DiamondElement extends BaseElement {
  type: "diamond";
  text?: string;
  fontSize?: number;
  fontFamily?: number;
  textAlign?: "left" | "center" | "right";
  verticalAlign?: "top" | "middle" | "bottom";
}

Arrow

interface ArrowElement extends BaseElement {
  type: "arrow";
  points: [number, number][];  // Array of [x, y] coordinates relative to element
  startBinding: Binding | null;
  endBinding: Binding | null;
  roundness: { type: 2 };      // 2 = curved arrow
}

Example:

{
  "id": "arrow1",
  "type": "arrow",
  "x": 100,
  "y": 100,
  "width": 200,
  "height": 0,
  "points": [
    [0, 0],
    [200, 0]
  ],
  "roundness": { "type": 2 },
  "startBinding": null,
  "endBinding": null
}

Points explanation:

  • First point [0, 0] is relative to (x, y)
  • Subsequent points are relative to the first point
  • For straight horizontal arrow: [[0, 0], [width, 0]]
  • For straight vertical arrow: [[0, 0], [0, height]]

Line

interface LineElement extends BaseElement {
  type: "line";
  points: [number, number][];
  startBinding: Binding | null;
  endBinding: Binding | null;
  roundness: { type: 2 } | null;
}

Text

interface TextElement extends BaseElement {
  type: "text";
  text: string;
  fontSize: number;
  fontFamily: number;          // 1-3
  textAlign: "left" | "center" | "right";
  verticalAlign: "top" | "middle" | "bottom";
  roundness: null;             // Text has no roundness
}

Example:

{
  "id": "text1",
  "type": "text",
  "x": 100,
  "y": 100,
  "width": 150,
  "height": 25,
  "text": "Hello World",
  "fontSize": 20,
  "fontFamily": 1,
  "textAlign": "left",
  "verticalAlign": "top",
  "roundness": null
}

Width/Height calculation:

  • Width ≈ text.length * fontSize * 0.6
  • Height ≈ fontSize * 1.2 * numberOfLines

Bindings

Bindings connect arrows to shapes:

interface Binding {
  elementId: string;           // ID of bound element
  focus: number;               // -1 to 1, position along edge
  gap: number;                 // Distance from element edge
}

Common Colors

Color Name Hex Code Use Case
Black #1e1e1e Default stroke
Light Blue #a5d8ff Primary entities
Light Green #b2f2bb Process steps
Yellow #ffd43b Important/Central
Light Red #ffc9c9 Warnings/Errors
Cyan #96f2d7 Secondary items
Transparent transparent No fill
White #ffffff Background

ID Generation

IDs should be unique strings. Common patterns:

// Timestamp-based
const id = Date.now().toString(36) + Math.random().toString(36).substr(2);

// Sequential
const id = "element-" + counter++;

// Descriptive
const id = "step-1", "entity-user", "arrow-1-to-2";

Seed Generation

Seeds are used for deterministic randomness in hand-drawn effect:

const seed = Math.floor(Math.random() * 2147483647);

Version and VersionNonce

const version = 1;  // Increment when element is edited
const versionNonce = Math.floor(Math.random() * 2147483647);

Coordinate System

  • Origin (0, 0) is top-left corner
  • X increases to the right
  • Y increases downward
  • All units are in pixels
Context Spacing
Horizontal gap between elements 200-300px
Vertical gap between rows 100-150px
Minimum margin from edge 50px
Arrow-to-box clearance 20-30px

Font Families

ID Name Description
1 Virgil Hand-drawn style (default)
2 Helvetica Clean sans-serif
3 Cascadia Monospace

Validation Rules

Required:

  • All IDs must be unique
  • type must match actual element type
  • version must be an integer ≥ 1
  • opacity must be 0-100

⚠️ Recommended:

  • Keep roughness at 1 for consistency
  • Use strokeWidth of 2 for clarity
  • Set isDeleted to false
  • Set locked to false
  • Keep frameId, boundElements, link as null

Complete Minimal Example

{
  "type": "excalidraw",
  "version": 2,
  "source": "https://excalidraw.com",
  "elements": [
    {
      "id": "box1",
      "type": "rectangle",
      "x": 100,
      "y": 100,
      "width": 200,
      "height": 100,
      "angle": 0,
      "strokeColor": "#1e1e1e",
      "backgroundColor": "#a5d8ff",
      "fillStyle": "solid",
      "strokeWidth": 2,
      "strokeStyle": "solid",
      "roughness": 1,
      "opacity": 100,
      "groupIds": [],
      "frameId": null,
      "index": "a0",
      "roundness": { "type": 3 },
      "seed": 1234567890,
      "version": 1,
      "versionNonce": 987654321,
      "isDeleted": false,
      "boundElements": null,
      "updated": 1706659200000,
      "link": null,
      "locked": false,
      "text": "Hello",
      "fontSize": 20,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle"
    }
  ],
  "appState": {
    "viewBackgroundColor": "#ffffff",
    "gridSize": 20
  },
  "files": {}
}