Skip to content

drawmode

LLMs write TypeScript, not raw JSON. One tool, typed SDK, zero prompt engineering.

Code Mode

LLM writes ~10 lines of TypeScript against a typed SDK. The SDK handles all Excalidraw complexity — labels, bindings, arrow math.

Automatic Layout

Graphviz dot engine (Sugiyama algorithm) with crossing minimization and orthogonal edge routing.

28 Color Presets

10 semantic presets + 18 cloud provider presets for AWS, Azure, GCP, and Kubernetes.

Multiple Outputs

.excalidraw files, shareable excalidraw.com URLs, PNG images, and SVG exports.

Edit Existing Diagrams

Diagram.fromFile() loads .excalidraw files for modification — find, update, and remove nodes.

Deploy Anywhere

Local stdio, local HTTP, or Cloudflare Workers for remote MCP access.

Traditional MCP diagram tools ask the LLM to produce raw Excalidraw JSON — hundreds of lines with pixel coordinates, bound text element pairs, arrow binding math, and edge routing. This is fragile and error-prone.

drawmode flips the approach: the LLM writes ~10 lines of TypeScript against a typed SDK. The SDK handles all the Excalidraw complexity (labels need two elements, arrows need binding math, elbow routing needs specific flags). Graphviz handles layout. The result is always valid.

Traditional: LLM → 500 lines of JSON (~25,000 tokens) → broken diagrams
drawmode: LLM → 10 lines of TypeScript (~500 tokens) → SDK + Graphviz → valid diagrams

This ~50x context compression is not accidental — it’s the core thesis. Code is the optimal serialization format for LLM context windows because it’s semantically dense, composable, and LLM-native. Read more in Code-First Context Management.

const d = new Diagram();
const api = d.addBox("API Gateway", { row: 0, col: 1, color: "backend" });
const db = d.addBox("Postgres", { row: 1, col: 0, color: "database" });
const cache = d.addBox("Redis", { row: 1, col: 2, color: "cache" });
d.connect(api, db, "queries");
d.connect(api, cache, "reads", { style: "dashed" });
d.addGroup("Data Layer", [db, cache]);
return d.render({ format: "url" });