Excalidraw Diagram Generator
This skill generates Excalidraw diagrams programmatically using Python. Instead of creating ASCII diagrams, use this to produce professional-looking, editable diagrams.
Output format: .excalidraw JSON files that can be:
- Opened at https://excalidraw.com (drag & drop the file)
- Edited in VS Code with the Excalidraw extension
- Embedded in documentation
- Exported to SVG/PNG for embedding in Google Docs, presentations, etc.
Quick Start
Method 1: Direct Python Script (Recommended)
Write a Python script using the generator library and run it:
python
1#!/usr/bin/env python3
2import sys
3import os
4sys.path.insert(0, os.path.expanduser("~/.claude/skills/excalidraw-diagrams/scripts"))
5from excalidraw_generator import Diagram, Flowchart, ArchitectureDiagram
6
7# Create your diagram
8d = Diagram()
9box1 = d.box(100, 100, "Step 1", color="blue")
10box2 = d.box(300, 100, "Step 2", color="green")
11d.arrow_between(box1, box2, "next")
12d.save("my_diagram.excalidraw")
Run with:
bash
1python3 /path/to/your_script.py
Method 2: Inline Python Execution
bash
1python3 -c "
2import sys, os
3sys.path.insert(0, os.path.expanduser('~/.claude/skills/excalidraw-diagrams/scripts'))
4from excalidraw_generator import Diagram
5
6d = Diagram()
7a = d.box(100, 100, 'Hello', color='blue')
8b = d.box(300, 100, 'World', color='green')
9d.arrow_between(a, b)
10d.save('hello.excalidraw')
11print('Created: hello.excalidraw')
12"
API Reference
Diagram Class
The base class for all diagrams.
python
1from excalidraw_generator import Diagram
2
3d = Diagram(background="#ffffff") # white background
Methods
box(x, y, label, width=150, height=60, color="blue", shape="rectangle", font_size=18)
Create a labeled shape. Returns an Element for connecting.
x, y: Position (top-left corner)
label: Text to display
color: "blue", "green", "red", "yellow", "orange", "violet", "cyan", "teal", "gray", "black"
shape: "rectangle", "ellipse", "diamond"
python
1box1 = d.box(100, 100, "Process A", color="blue")
2box2 = d.box(100, 200, "Decision?", color="yellow", shape="diamond")
text_box(x, y, content, font_size=20, color="black")
Create standalone text.
python
1d.text_box(100, 50, "System Architecture", font_size=28, color="black")
arrow_between(source, target, label=None, color="black", from_side="auto", to_side="auto")
Draw an arrow between two elements.
from_side, to_side: "auto", "left", "right", "top", "bottom"
python
1d.arrow_between(box1, box2, "sends data")
2d.arrow_between(box1, box3, from_side="bottom", to_side="top")
line_between(source, target, color="black")
Draw a line (no arrowhead) between elements.
save(path)
Save the diagram. Extension .excalidraw added if not present.
python
1d.save("output/my_diagram") # Creates output/my_diagram.excalidraw
Configuration & Styling
The diagram generator supports extensive customization through configuration classes.
DiagramStyle - Global Appearance
Control the overall look of all elements in the diagram:
python
1from excalidraw_generator import Diagram, DiagramStyle
2
3# Create a clean, professional diagram
4d = Diagram(diagram_style=DiagramStyle(
5 roughness=0, # 0=clean, 1=hand-drawn, 2=rough sketch
6 stroke_style="solid", # "solid", "dashed", "dotted"
7 stroke_width=2, # Line thickness (1-4)
8 color_scheme="corporate" # Named color scheme
9))
Roughness levels:
0 - Architect: Clean, precise lines
1 - Artist: Normal hand-drawn look (default)
2 - Cartoonist: Rough, sketchy appearance
Color Schemes
Use semantic colors from predefined schemes:
python
1# Available schemes: "default", "monochrome", "corporate", "vibrant", "earth"
2d = Diagram(diagram_style=DiagramStyle(color_scheme="vibrant"))
3
4# Get colors by role
5primary = d.scheme_color("primary") # Main components
6secondary = d.scheme_color("secondary") # Supporting elements
7accent = d.scheme_color("accent") # Highlights
8warning = d.scheme_color("warning") # Caution states
9danger = d.scheme_color("danger") # Error states
10neutral = d.scheme_color("neutral") # Backgrounds, users
Scheme color mappings:
| Scheme | Primary | Secondary | Accent | Warning | Danger |
|---|
| default | blue | green | violet | yellow | red |
| monochrome | black | gray | gray | gray | black |
| corporate | blue | teal | violet | orange | red |
| vibrant | violet | cyan | orange | yellow | red |
| earth | teal | green | orange | yellow | red |
FlowchartStyle - Flowchart Customization
Customize flowchart node colors and routing behavior:
python
1from excalidraw_generator import Flowchart, FlowchartStyle
2
3fc = Flowchart(flowchart_style=FlowchartStyle(
4 start_color="cyan", # Start node color
5 end_color="red", # End node color
6 process_color="blue", # Process node color
7 decision_color="orange", # Decision diamond color
8))
ArchitectureStyle - Architecture Diagram Customization
python
1from excalidraw_generator import ArchitectureDiagram, ArchitectureStyle
2
3arch = ArchitectureDiagram(architecture_style=ArchitectureStyle(
4 component_color="blue",
5 database_color="green",
6 service_color="violet",
7 user_color="gray",
8))
BoxStyle - Text Box Sizing
Control automatic text box sizing:
python
1from excalidraw_generator import Diagram, BoxStyle
2
3d = Diagram(box_style=BoxStyle(
4 h_padding=40, # Horizontal padding (total)
5 v_padding=24, # Vertical padding (total)
6 min_width=80, # Minimum box width
7 min_height=40, # Minimum box height
8 font_size=18, # Default font size
9 font_family="hand" # "hand", "normal", "code"
10))
Flowchart Class
Specialized for flowcharts with automatic positioning.
python
1from excalidraw_generator import Flowchart
2
3fc = Flowchart(direction="vertical", spacing=80)
4
5fc.start("Begin")
6fc.process("p1", "Process Data")
7fc.decision("d1", "Valid?")
8fc.process("p2", "Save")
9fc.end("Done")
10
11fc.connect("__start__", "p1")
12fc.connect("p1", "d1")
13fc.connect("d1", "p2", label="Yes")
14fc.connect("d1", "__end__", label="No")
15
16fc.save("flowchart.excalidraw")
Methods
start(label="Start") - Green ellipse
end(label="End") - Red ellipse
process(node_id, label, color="blue") - Blue rectangle
decision(node_id, label, color="yellow") - Yellow diamond
node(node_id, label, shape, color, width, height) - Generic node
connect(from_id, to_id, label=None) - Arrow between nodes
position_at(x, y) - Set position for next node
AutoLayoutFlowchart Class
For complex flowcharts with automatic hierarchical layout. Requires grandalf package.
python
1from excalidraw_generator import AutoLayoutFlowchart, DiagramStyle, FlowchartStyle, LayoutConfig
2
3fc = AutoLayoutFlowchart(
4 diagram_style=DiagramStyle(roughness=0), # Clean lines
5 flowchart_style=FlowchartStyle(decision_color="orange"),
6 layout_config=LayoutConfig(
7 vertical_spacing=100,
8 horizontal_spacing=80,
9 )
10)
11
12# Add nodes with semantic types
13fc.add_node("start", "Start", shape="ellipse", color="green", node_type="terminal")
14fc.add_node("process1", "Validate Input", node_type="process")
15fc.add_node("check", "Is Valid?", shape="diamond", color="yellow", node_type="decision")
16fc.add_node("success", "Process Data", node_type="process")
17fc.add_node("error", "Show Error", color="red", node_type="process")
18fc.add_node("end", "End", shape="ellipse", color="red", node_type="terminal")
19
20# Add edges (arrows)
21fc.add_edge("start", "process1")
22fc.add_edge("process1", "check")
23fc.add_edge("check", "success", label="Yes")
24fc.add_edge("check", "error", label="No")
25fc.add_edge("success", "end")
26fc.add_edge("error", "process1", label="Retry") # Back-edge auto-routes through whitespace
27
28# Compute layout and render
29result = fc.compute_layout(
30 two_column=True, # Split tall diagrams into columns
31 target_aspect_ratio=0.8, # Target width/height ratio
32)
33
34fc.save("auto_flowchart.excalidraw")
Node types for routing:
terminal - Start/end nodes
process - Standard processing steps
decision - Decision diamonds (arrows exit from sides)
Methods
add_node(node_id, label, shape, color, width, height, node_type) - Add a node
add_edge(from_id, to_id, label, color) - Add an edge
compute_layout(start_x, start_y, max_width, max_height, routing, two_column, target_aspect_ratio, column_gap) - Auto-position nodes
ArchitectureDiagram Class
For system architecture diagrams.
python
1from excalidraw_generator import ArchitectureDiagram
2
3arch = ArchitectureDiagram()
4
5# Add components at specific positions
6arch.user("user", "User", x=100, y=200)
7arch.component("frontend", "React App", x=250, y=200, color="blue")
8arch.service("api", "API Gateway", x=450, y=200, color="violet")
9arch.database("db", "PostgreSQL", x=650, y=200, color="green")
10
11# Connect them
12arch.connect("user", "frontend", "HTTPS")
13arch.connect("frontend", "api", "REST")
14arch.connect("api", "db", "SQL")
15
16arch.save("architecture.excalidraw")
Methods
component(id, label, x, y, width=150, height=80, color="blue")
database(id, label, x, y, color="green") - Ellipse shape
service(id, label, x, y, color="violet")
user(id, label="User", x=100, y=100) - Gray ellipse
connect(from_id, to_id, label=None, bidirectional=False)
Color Reference
Available colors (stroke color, with matching light background):
| Color | Stroke Hex | Use For |
|---|
blue | #1971c2 | Primary components |
green | #2f9e44 | Success, databases |
red | #e03131 | Errors, end states |
yellow | #f08c00 | Warnings, decisions |
orange | #e8590c | Highlights |
violet | #6741d9 | Services |
cyan | #0c8599 | Network |
teal | #099268 | Secondary |
gray | #868e96 | Users, actors |
black | #1e1e1e | Text, arrows |
Complete Examples
Example 1: Simple Flow
python
1import sys, os
2sys.path.insert(0, os.path.expanduser("~/.claude/skills/excalidraw-diagrams/scripts"))
3from excalidraw_generator import Diagram
4
5d = Diagram()
6
7# Title
8d.text_box(200, 30, "Data Processing Pipeline", font_size=24)
9
10# Boxes
11input_box = d.box(100, 100, "Input", color="gray")
12process = d.box(300, 100, "Process", color="blue")
13output = d.box(500, 100, "Output", color="green")
14
15# Arrows
16d.arrow_between(input_box, process, "raw data")
17d.arrow_between(process, output, "results")
18
19d.save("pipeline.excalidraw")
Example 2: Decision Flowchart
python
1import sys, os
2sys.path.insert(0, os.path.expanduser("~/.claude/skills/excalidraw-diagrams/scripts"))
3from excalidraw_generator import Flowchart
4
5fc = Flowchart(direction="vertical", spacing=100)
6
7fc.start("User Request")
8fc.process("auth", "Authenticate")
9fc.decision("valid", "Valid Token?")
10
11# Branch for Yes
12fc.position_at(300, 340)
13fc.process("proc", "Process Request")
14fc.process("resp", "Return Response")
15
16# Branch for No
17fc.position_at(100, 340)
18fc.process("err", "Return 401")
19
20fc.connect("__start__", "auth")
21fc.connect("auth", "valid")
22fc.connect("valid", "proc", "Yes")
23fc.connect("valid", "err", "No")
24fc.connect("proc", "resp")
25
26fc.save("auth_flow.excalidraw")
Example 3: Microservices Architecture
python
1import sys, os
2sys.path.insert(0, os.path.expanduser("~/.claude/skills/excalidraw-diagrams/scripts"))
3from excalidraw_generator import ArchitectureDiagram
4
5arch = ArchitectureDiagram()
6
7# Client layer
8arch.user("client", "Client", x=400, y=50)
9
10# Gateway
11arch.service("gateway", "API Gateway", x=350, y=180, color="violet")
12
13# Services row
14arch.service("auth", "Auth Service", x=100, y=350, color="blue")
15arch.service("users", "User Service", x=300, y=350, color="blue")
16arch.service("orders", "Order Service", x=500, y=350, color="blue")
17arch.service("notify", "Notification", x=700, y=350, color="cyan")
18
19# Databases
20arch.database("authdb", "Auth DB", x=100, y=500, color="green")
21arch.database("userdb", "User DB", x=300, y=500, color="green")
22arch.database("orderdb", "Order DB", x=500, y=500, color="green")
23
24# Message queue
25arch.component("queue", "Message Queue", x=600, y=450, color="orange")
26
27# Connections
28arch.connect("client", "gateway", "HTTPS")
29arch.connect("gateway", "auth", "gRPC")
30arch.connect("gateway", "users", "gRPC")
31arch.connect("gateway", "orders", "gRPC")
32arch.connect("auth", "authdb", "SQL")
33arch.connect("users", "userdb", "SQL")
34arch.connect("orders", "orderdb", "SQL")
35arch.connect("orders", "queue", "publish")
36arch.connect("queue", "notify", "subscribe")
37
38arch.save("microservices.excalidraw")
Viewing the Output
After generating a .excalidraw file:
- Excalidraw.com: Go to https://excalidraw.com and drag the file onto the canvas
- VS Code: Install the "Excalidraw" extension, then open the file
- CLI: Use
open filename.excalidraw on macOS to open with default app
Exporting to PNG
To embed diagrams in Google Docs, presentations, or other documents, export them to PNG using Playwright.
Using the Export Script
bash
1# First time setup: install dependencies
2cd ~/.claude/skills/excalidraw-diagrams/scripts
3npm install
4npx playwright install chromium
5
6# Export to PNG
7node ~/.claude/skills/excalidraw-diagrams/scripts/export_playwright.js diagram.excalidraw output.png
How It Works
The Playwright export script:
- Opens excalidraw.com in a headless Chromium browser
- Loads your diagram via drag-and-drop simulation
- Fits the view to content (Shift+1)
- Screenshots the canvas at 1920x1200 resolution
Requirements
- Node.js: Version 18 or later
- Playwright: Installed via
npm install in the scripts directory
- Chromium: Installed via
npx playwright install chromium
Tips
-
Positioning: Use a grid system. Start shapes at multiples of 50 or 100 for alignment.
-
Spacing: Leave 50-100px between elements for clean arrows.
-
Labels: Keep labels short (2-3 words). Use text boxes for longer descriptions.
-
Colors: Use consistent colors for similar components (all databases green, all services blue).
-
Layout patterns:
- Horizontal flow: x increases, y constant
- Vertical flow: y increases, x constant
- Grid: Combine both for complex diagrams
-
After generation: Open in Excalidraw to fine-tune positions and add hand-drawn elements.
Google Drive Integration
Save diagrams directly to Google Drive for cloud storage and human editing via Excalidraw web.
Quick Upload
python
1import sys, os
2sys.path.insert(0, os.path.expanduser("~/.claude/skills/excalidraw-diagrams/scripts"))
3from excalidraw_generator import Diagram
4
5d = Diagram()
6d.box(100, 100, "Cloud Diagram", color="blue")
7
8# Save directly to Google Drive
9result = d.save_to_drive("my_diagram.excalidraw", share_public=True)
10
11print(f"View in Drive: {result['file']['web_view_link']}")
12print(f"Edit in Excalidraw: {result['edit_url']}")
save_to_drive() Method
save_to_drive(name=None, folder_id=None, share_public=False, local_path=None)
Save diagram directly to Google Drive.
name: Filename in Drive (default: "diagram.excalidraw")
folder_id: Drive folder ID to upload to (default: root)
share_public: If True, make file publicly accessible
local_path: Also save locally to this path (optional)
Returns a dict with:
file.id: Google Drive file ID
file.web_view_link: Link to view in Drive
edit_url: Link to open in Excalidraw.com
share: Sharing info (if share_public=True)
Using drive_helper Directly
For more control, use the drive_helper module:
python
1import sys, os
2sys.path.insert(0, os.path.expanduser("~/.claude/skills/excalidraw-diagrams/scripts"))
3from excalidraw_generator import Diagram
4from drive_helper import upload_to_drive, share_file, search_excalidraw_files
5
6# Create and save locally first
7d = Diagram()
8d.box(100, 100, "Hello")
9d.save("hello.excalidraw")
10
11# Upload to specific folder
12result = upload_to_drive("hello.excalidraw", folder_id="YOUR_FOLDER_ID")
13
14# Share with specific user
15share_file(result["file"]["id"], email="user@example.com", role="writer")
16
17# Find all excalidraw files in Drive
18files = search_excalidraw_files()
DriveUploader Class
For batch uploads or folder organization:
python
1from drive_helper import DriveUploader
2
3uploader = DriveUploader(folder_id="YOUR_FOLDER_ID")
4
5result = uploader.upload(
6 "diagram.excalidraw",
7 name="My Diagram",
8 share_public=True
9)
Human Editing Workflow
After saving to Drive, users can edit diagrams in two ways:
-
Excalidraw.com (via edit_url):
- Open
edit_url in browser
- Excalidraw loads the file from Drive
- Edit and save changes
-
gdrive.excalidraw.com:
Round-Trip Workflow
Create with Claude → Human edits → Claude updates:
python
1from drive_helper import download_from_drive, update_in_drive
2
3# Download human-edited version
4download_from_drive(file_id, "updated_diagram.excalidraw")
5
6# Make programmatic changes
7d = Diagram()
8# ... load and modify ...
9d.save("updated_diagram.excalidraw")
10
11# Upload changes back
12update_in_drive(file_id, "updated_diagram.excalidraw")
Complete Workflow: Diagram to Google Doc
Create a diagram, export to PNG, and embed in a Google Doc:
python
1import sys, os, subprocess, json
2sys.path.insert(0, os.path.expanduser("~/.claude/skills/excalidraw-diagrams/scripts"))
3from excalidraw_generator import ArchitectureDiagram
4
5# 1. Create the diagram
6arch = ArchitectureDiagram()
7arch.user("user", "User", x=50, y=150)
8arch.component("frontend", "Frontend", x=200, y=130, color="blue")
9arch.service("api", "API Gateway", x=400, y=130, color="violet")
10arch.database("db", "Database", x=600, y=150, color="green")
11arch.connect("user", "frontend", "HTTPS")
12arch.connect("frontend", "api", "REST")
13arch.connect("api", "db", "SQL")
14
15# 2. Save locally and to Drive
16arch.save("/tmp/architecture.excalidraw")
17drive_result = arch.save_to_drive("architecture.excalidraw", share_public=True)
18file_id = drive_result["file"]["id"]
19edit_url = drive_result["edit_url"]
20
21# 3. Export to PNG using Playwright
22subprocess.run([
23 "node", os.path.expanduser("~/.claude/skills/excalidraw-diagrams/scripts/export_playwright.js"),
24 "/tmp/architecture.excalidraw", "/tmp/architecture.png"
25])
26
27# 4. Upload PNG to Drive and share
28drive_script = os.path.expanduser("~/.claude/skills/google-docs/scripts/drive_manager.rb")
29png_result = subprocess.run(
30 ["ruby", drive_script, "upload", "--file", "/tmp/architecture.png"],
31 capture_output=True, text=True
32)
33png_data = json.loads(png_result.stdout)
34png_id = png_data["file"]["id"]
35
36# Share the PNG publicly
37subprocess.run(["ruby", drive_script, "share", "--file-id", png_id, "--type", "anyone", "--role", "reader"])
38png_url = f"https://drive.google.com/uc?id={png_id}&export=download"
39
40# 5. Create Google Doc with embedded image
41docs_script = os.path.expanduser("~/.claude/skills/google-docs/scripts/docs_manager.rb")
42
43# Create document
44doc_input = json.dumps({"title": "Architecture Overview", "content": "System Architecture\\n\\n"})
45doc_result = subprocess.run(
46 ["ruby", docs_script, "create"],
47 input=doc_input, capture_output=True, text=True
48)
49doc_data = json.loads(doc_result.stdout)
50doc_id = doc_data["document_id"]
51
52# Insert image (Note: SVG not supported - must use PNG)
53# Width 468pt fits standard Google Doc margins; height auto-scales
54img_input = json.dumps({
55 "document_id": doc_id,
56 "image_url": png_url,
57 "index": 25,
58 "width": 468 # Page width in points (fits default margins)
59})
60subprocess.run(["ruby", docs_script, "insert-image"], input=img_input, capture_output=True, text=True)
61
62# Append edit link
63link_input = json.dumps({"document_id": doc_id, "text": f"\\n\\nEdit diagram: {edit_url}"})
64subprocess.run(["ruby", docs_script, "append"], input=link_input, capture_output=True, text=True)
65
66print(f"Document: https://docs.google.com/document/d/{doc_id}/edit")
67print(f"Edit diagram: {edit_url}")
Important: Google Docs does not support SVG images. Always export to PNG for embedding.
Prerequisites
- Google Docs skill must be installed (provides OAuth and drive_manager.rb)
- First run will prompt for Google authorization if not already done