DrawFather
# DrawFather
<img src="assets/brand/drawfather-mascot-v2.png" width="760" alt="DrawFather: a hand-drawn don with a robotic drawing hand sketching an Agent, API, database and Docs diagram in the air." />
**Hand-drawn visuals for AI agents.**
[drawfather.com](https://drawfather.com)
[Launch roadmap](ROADMAP.md) · [Contributing](CONTRIBUTING.md) · [Licensing](LICENSING.md)
Launch verification is in progress. See [Phase 8 evidence and remaining gates](PHASE8.md), [local client setup](docs/clients/README.md), and [container setup](release/CONTAINER.md). No npm release or MCP registry listing is published yet.
Agents can start with `get_capabilities` or the [DrawFather skill](skills/drawfather/SKILL.md). Routine tool replies contain compact metadata; source, images, and inline exports are explicit options. See the [agent API](schema/AGENT_API.md).
Mascot concept: AI-generated brand artwork. The example diagram below is rendered by DrawFather from [editable JSON](examples/hello.drawfather).
DrawFather is a source-available MCP server that creates sketch-style drawings from editable JSON. Your AI assistant calls the tools; DrawFather saves a `.drawfather` file and exports SVG, PNG, JPG, WebP, or vector PDF. No drawing interface, account, API key, or model download is required.
The engine uses [Elastic License 2.0](LICENSE), a fair-code license. The [.drawfather JSON schema](schema/) and [examples](examples/) use MIT so the file format can be implemented independently. Read [LICENSING.md](LICENSING.md) for permitted use, hosted-service restrictions, and drawing output rights.
## Describe it. DrawFather composes it.
Give your agent the steps or the connections. DrawFather measures the lettering, sizes the shapes, saves editable `.drawfather` source, and returns a compact result. Request `preview: true` for a PNG or `includeSource: true` for the complete source in the response.
```json
{
"name": "product-launch",
"design": {
"template": "process",
"title": "From signup to first value",
"steps": ["Sign up", "Verify email", "Create workspace", "First useful action"]
}
}
```
Call `compose_drawing` with this input:

Three general composition templates are included: **process** for 2–24 ordered steps, **cycle** for 3–12 steps with curved connections, and **architecture** for up to 48 nodes and 96 connections. Process and architecture accept `direction: "auto"`, `"horizontal"`, or `"vertical"`. Labels, optional details, and node shapes determine the required space. Auto process flows wrap after four steps per row; larger architectures default to vertical.
Each named step can be a string or an object with `id`, `label`, optional `detail`, and `shape` (`rectangle`, `ellipse`, or `diamond`). Architecture uses these node objects plus connections with `from`, `to`, and an optional `label`. Supply explicit IDs when you want to edit particular elements later; `df-` is reserved for generated elements. All templates accept an optional `title`, `subtitle`, and deterministic `seed`.
Use `list_templates` to discover complete inputs, or inspect the MIT-licensed [composition schema](schema/composition-v1.schema.json). New compositions use native v2: the file retains its creation request, current semantic model, resolved elements, and constrained view. Reopen the file and use `edit_drawing` with `reflow` to regenerate the current model without supplying another request.
Use `edit_drawing` for semantic changes, pins, groups, and constraints; `update_drawing` supports complete-element geometry edits. To generate a new layout in an existing file, call `compose_drawing` with its current `expectedRevision` and a complete `design`. Explicit composition replacement replaces its content; existing pins and surviving layout constraints remain protected. Omit `expectedRevision` to create a new drawing without overwriting an existing file.
## Ten technical diagram families
Use `compose_technical` for **flowcharts, sequence diagrams, C4 architecture, ER models, state machines, class/domain models, data flows, dependency graphs, swimlane activities, and deployment/network diagrams**. Typed notation retains fields, keys, cardinalities, guards, message order, ownership and boundaries in editable JSON. Shared named views, hand-drawn symbols, callouts, legends and layers support focused explanations.
Discover `get_capabilities` with `topic: "technical"`, then `recipe: "oauth"`, `"ci-cd"`, `"microservices"`, `"event-driven"`, `"kubernetes"`, `"rag"` or another catalog ID for a complete example. Read the [technical contract](schema/TECHNICAL.md) and browse [twenty generated examples with before/after source, images and edit code](examples/technical/README.md). Import Mermaid flowchart, sequence and ER code, then hand off editable Excalidraw or draw.io scenes. See the [conversion examples with images and code](examples/interchange/README.md) and [support/fidelity matrix](schema/INTERCHANGE.md).
## Eight creative diagram families
Use `compose_creative` for **mind maps, Gantt schedules, timelines, user journeys, Kanban boards, annotated screenshots, storyboards and wireframes**. Revise typed items with `edit_creative`: expand a branch, change a date, move the same card, or update a panel caption. Saved drawings double as reusable templates through `reuseFrom`, with component libraries and shared brand presets.

Browse [the creative gallery](examples/creative/README.md) for images, editable source and before/after edit code. The [creative contract](schema/CREATIVE.md) covers portable embedded images/SVG assets, local fonts and explicit glyph fallback, rotation/layers, accessible summaries, selectable SVG/PDF text and vector PDF page controls. [Phase 7 evidence](PHASE7.md) records local verification and remaining launch boundaries.
## Revise a diagram while preserving its layout
Add a cache, rename the API, and remove an obsolete dependency through one revision-checked edit. Unaffected shapes and annotations keep their geometry; pinned elements keep their exact positions.

[Before image](examples/semantic-before.svg) · [Before source](examples/semantic-before.drawfather) · [Edited source](examples/semantic-after.drawfather) · [Complete edit batch](examples/semantic-edits.json)
Save the before source as `system` with `create_drawing`, then call `edit_drawing`:
```json
{
"name": "system",
"expectedRevision": 1,
"operations": [
{
"op": "insert_between",
"connectionId": "df-edge-2",
"node": { "id": "redis", "label": "Redis", "role": "database" },
"secondConnectionId": "redis-db"
},
{ "op": "rename", "id": "api", "label": "Core API" },
{ "op": "disconnect", "id": "df-edge-4" }
]
}
```
Use `group` and `regroup` for nested containers; `pin` and `unpin` for fixed origins; and `constraint` for relative placement or alignment. Changed shapes grow to fit their labels, affected connections are rerouted, and conflicting constraints return an actionable error without saving a partial edit. Use `reflow` to lay out selected nodes or the entire current model. See the [operation reference](schema/EDITS.md).
`drawing_history` lists local snapshots and migration backups. Its `undo`, `redo`, and `restore` actions require the current revision, create a new revision, and survive server restarts. Existing v1 drawings remain readable and renderable; `migrate_drawing` or the first successful semantic edit creates a byte-for-byte backup before upgrading them. See [migration and recovery](schema/MIGRATIONS.md).
## Inspect and preview repairs
Call `check_drawing` to find overlaps, clipped strokes, small or overflowing text, blocked connectors, group-boundary problems, poor contrast, and readability issues at intended output dimensions. Findings include severity, affected IDs, and suggested corrections.
Call `repair_drawing` with `action: "preview"` and the current `expectedRevision`. It proposes bounded spacing, label growth, rerouting, container fitting, and canvas fitting without changing files. Request `preview: true` and `includeSource: true` when reviewing the proposed image and source, then apply the returned `planId` with the same revision and repair/check options. Application is one undoable revision. Pins, constraints, annotations, seeds, and explicitly permitted overlaps are preserved; unresolved errors block the write.

[Before image](examples/visual-before.svg) · [Editable source](examples/visual-after.drawfather) · [Before/after reports](examples/visual-report.json) · [Visual checking and repair reference](schema/VISUAL.md)
For intended layering, use `allow_overlap` with an `ids` pair in `edit_drawing`, or supply `checks.allowOverlaps` during inspection and repair. Checking output dimensions is separate from export `options.scale`, which controls actual delivery size.
## Read and patch only what changed
Read one element with `get_drawing` using `view: "elements", ids: ["api"]`. Then patch its label or partial style without sending its coordinates again:
```json
{
"name": "system",
"expectedRevision": 1,
"patches": [{ "id": "api", "label": "Core API", "style": { "fill": "#d3e1f5" } }]
}
```
Call `patch_drawing` with this input. Results report changed IDs, revision, diagnostic counts/findings, and source paths. `get_drawing` defaults to a summary; `view: "graph"` pages semantic relationships and `view: "source"` retrieves the complete editable document. Selected reads, lists, diagnostics, and history use cursors tied to the current source and query.
Use `preview_drawing` for a PNG with explicit size, crop or selected IDs, padding and background. Exports return their local paths by default; `inline: true` embeds SVG text or a raster preview. These controls preserve the source and full-resolution export. See the [API contract](schema/AGENT_API.md), [agent skill](skills/drawfather/SKILL.md), and [seven-call reference task](benchmarks/README.md).
## Maintain drawings with the repository
A [project manifest](drawfather.project.json) links native sources, stable images, export settings and documentation. `project_diagrams` exposes the same check/render/affected/diff workflow through MCP; the CLI supports batch builds and CI:
```sh
drawfather validate --manifest drawfather.project.json
drawfather render --manifest drawfather.project.json --write-docs
drawfather check --manifest drawfather.project.json
drawfather affected --manifest drawfather.project.json --base origin/main
drawfather diff --manifest drawfather.project.json --id system --base origin/main
```
Stable filenames keep Markdown embeds intact; optional revision copies retain earlier source revisions. Results include relative Markdown links, alt text, captions, dimensions and editable-source backlinks. Generated blocks preserve surrounding prose; existing gallery-style embeds can be checked without being rewritten.
Code paths, symbols and supplied commit IDs are recorded in the manifest. Missing/stale images and documentation are separate from source relationships that need review. Rendering never clears required code review. After inspecting the relationships, acknowledge the exact source/reference hashes returned by the check. A fresh image or acknowledgement alone cannot establish architectural accuracy.
Exports support source, light, dark, paper and transparent backgrounds, scale settings, and explicit source/reference metadata. Metadata defaults to none; enabling it discloses the declared references or full native JSON in SVG/XMP. The source stays unchanged.
See the [runnable repository example](examples/project/README.md), [README/architecture/onboarding/ADR examples](examples/project/docs/architecture.md), [GitHub Actions recipe](examples/project/github-actions.yml), and [project contract](schema/PROJECT.md). `npm run docs:check` verifies this repository's gallery and managed example against their committed receipts.
## Choose the sketch character
`sketch` controls how hand-drawn the strokes feel. `roundness` controls rectangle corners separately:
| Sketch level | Appearance |
| --- | --- |
| `tidy` | Restrained stroke variation and straighter edges |
| `handwritten` | Gently bowed edges, uneven corners, and visible pencil-like strokes |
| `loose` | Stronger wobble, curvature, and overlapping strokes |
```json
{
"template": "process",
"title": "Try, learn, repeat",
"sketch": "handwritten",
"roundness": 0.35,
"seed": 42,
"steps": ["Build", "Measure", "Learn"]
}
```
Pass this as `design` to `compose_drawing`. New compositions default to `sketch: "handwritten"` and `roundness: 0.25`. Roundness ranges from `0` (square) to `1` (fully rounded), expressed as a fraction of half the shorter side. Each node can override `sketch` or `roundness`; roundness applies to rectangular nodes.
In native `.drawfather` source, set `sketch` at the document level or override it on individual elements. Set `roundness` on a rectangle. Native documents without a sketch preset keep the original v1 stroke style; rectangles without roundness keep square corners. Change a saved drawing's preset with `update_drawing`, supplying its `expectedRevision` and `sketch`; `sketch: null` restores the original style. To change corner rounding, upsert the complete rectangle element.
Randomness is seeded, so the same source produces the same strokes. Editing a label or another element keeps the shape's contour stable. Change the document `seed` for a fresh variation; explicit element seeds pin individual strokes. Updating the seed preserves positions, while recomposing an architecture may also change layout tie-breaks. Presets affect shape and connector strokes; lettering keeps the readable Kalam font.
For fine tuning, element `roughness` remains in the range 0.5–3. With a preset, 1.2 is neutral: the effective stroke roughness is the preset's value multiplied by `roughness / 1.2`. Higher values add stroke variation. Rectangle edge curvature also comes from a seeded contour; changing corner roundness is independent of the preset.
See the [rendered comparison and editable source](examples/sketch-levels.drawfather) in the gallery below.
<!-- drawfather-gallery:start -->
## Example gallery
Every image below is rendered by DrawFather from the accompanying `.drawfather` JSON. Expand a code block to copy the full source, or download the source file and adapt it. All example sources and images are MIT licensed.
Regenerate every example image and this gallery with `npm run example`. PNG, JPG, and WebP versions are also generated in `output/`.
### Inspect and repair blocked connectors
Visual inspection finds two connectors crossing Storage. A bounded repair reroutes those connections while preserving every node, annotation, ID, and seed.

**Try asking your agent:** Check the diagram for visual defects, preview repairs, then apply the reviewed plan using its revision and plan ID.
[Editable source](examples/visual-after.drawfather) · [SVG image](examples/visual-after.svg)
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"revision": 0,
"title": "Dense branches",
"seed": 42,
"sketch": "handwritten",
"canvas": {
"width": 1100,
"height": 760,
"background": "#ffffff"
},
"version": 1,
"elements": [
{
"id": "api",
"x": 90,
"y": 260,
"width": 180,
"height": 100,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "API",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "web",
"x": 760,
"y": 100,
"width": 180,
"height": 100,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Web",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "jobs",
"x": 760,
"y": 330,
"width": 180,
"height": 100,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Jobs",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "events",
"x": 760,
"y": 560,
"width": 180,
"height": 100,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Events",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "blocker",
"x": 410,
"y": 240,
"width": 180,
"height": 100,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Storage",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "note",
"x": 90,
"y": 560,
"width": 260,
"height": 70,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Keep the annotations",
"fontSize": 24,
"type": "text",
"align": "left"
},
{
"id": "api-web",
"type": "arrow",
"from": {
"elementId": "api",
"anchor": "right"
},
"to": {
"elementId": "web",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"waypoints": [
[
311.2,
310
],
[
311.2,
150
],
[
718.8,
150
]
],
"text": "",
"fontSize": 24
},
{
"id": "api-jobs",
"type": "arrow",
"from": {
"elementId": "api",
"anchor": "right"
},
"to": {
"elementId": "jobs",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"waypoints": [
[
311.2,
310
],
[
311.2,
380
],
[
718.8,
380
]
],
"text": "",
"fontSize": 24
},
{
"id": "api-events",
"type": "arrow",
"from": {
"elementId": "api",
"anchor": "right"
},
"to": {
"elementId": "events",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
}
]
}
```
</details>
[Before image](examples/visual-before.svg) · [Before source](examples/visual-before.drawfather) · [Inspection reports](examples/visual-report.json)
### An architecture edited without a redraw
Insert Redis, rename the API, remove a legacy dependency, and create a nested cache group. The API and database stay pinned, and the client, worker, and heading retain their existing geometry.

**Try asking your agent:** Add Redis between the API and database, rename the API, remove the legacy dependency, and keep the rest in place.
[Editable source](examples/semantic-after.drawfather) · [SVG image](examples/semantic-after.svg)
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"revision": 0,
"title": "An architecture that can evolve",
"seed": 42,
"sketch": "handwritten",
"canvas": {
"width": 1680,
"height": 640,
"background": "#fffdf7"
},
"version": 2,
"elements": [
{
"id": "df-title",
"x": 64,
"y": 64,
"width": 1552,
"height": 73,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "An architecture that can evolve",
"fontSize": 36,
"type": "text",
"align": "left"
},
{
"id": "client",
"x": 88,
"y": 207,
"width": 271,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Client",
"fontSize": 24,
"type": "ellipse"
},
{
"id": "api",
"x": 573,
"y": 207,
"width": 184,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#d3e1f5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Core API",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "db",
"x": 1408,
"y": 207,
"width": 184,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#c6e6d5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Database",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "worker",
"x": 977,
"y": 207,
"width": 184,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#f5c8b6",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Worker",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "df-edge-1",
"type": "arrow",
"from": {
"elementId": "client",
"anchor": "right"
},
"to": {
"elementId": "api",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "df-label-1",
"x": 423,
"y": 258,
"width": 86,
"height": 47,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "request",
"fontSize": 18,
"type": "text",
"align": "center"
},
{
"id": "df-edge-2",
"type": "arrow",
"from": {
"elementId": "api",
"anchor": "right"
},
"to": {
"elementId": "redis",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"waypoints": [
[
783,
255
],
[
783,
494.9
],
[
1212.5,
494.9
]
],
"text": "",
"fontSize": 24
},
{
"id": "df-label-2",
"x": 961.75,
"y": 435.9,
"width": 72,
"height": 47,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "query",
"fontSize": 18,
"type": "text",
"align": "center"
},
{
"id": "df-edge-3",
"type": "arrow",
"from": {
"elementId": "api",
"anchor": "right"
},
"to": {
"elementId": "worker",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "df-label-3",
"x": 821,
"y": 205,
"width": 92,
"height": 47,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "enqueue",
"fontSize": 18,
"type": "text",
"align": "center"
},
{
"id": "backend",
"x": 541,
"y": 125.1,
"width": 1083,
"height": 482,
"stroke": "#6b7280",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Backend services",
"fontSize": 22,
"type": "container",
"padding": 32
},
{
"id": "redis",
"x": 1238.5,
"y": 446.9,
"width": 184,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#e2d4ef",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Redis",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "redis-db",
"type": "arrow",
"from": {
"elementId": "redis",
"anchor": "top"
},
"to": {
"elementId": "db",
"anchor": "bottom"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"waypoints": [
[
1330.5,
420.9
],
[
1500,
420.9
],
[
1500,
329
]
],
"text": "",
"fontSize": 24
},
{
"id": "cache",
"x": 1206.5,
"y": 367,
"width": 248,
"height": 208,
"stroke": "#6b7280",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Cache",
"fontSize": 22,
"type": "container",
"padding": 32
}
],
"model": {
"kind": "architecture",
"direction": "auto",
"origin": {
"source": "composition",
"composition": {
"title": "An architecture that can evolve",
"seed": 42,
"sketch": "handwritten",
"roundness": 0.25,
"template": "architecture",
"direction": "auto",
"nodes": [
{
"id": "client",
"label": "Client",
"shape": "ellipse",
"role": "person"
},
{
"id": "api",
"label": "API",
"shape": "rectangle",
"role": "service"
},
{
"id": "db",
"label": "Database",
"shape": "rectangle",
"role": "database"
},
{
"id": "worker",
"label": "Worker",
"shape": "rectangle",
"role": "service"
}
],
"connections": [
{
"from": "client",
"to": "api",
"label": "request"
},
{
"from": "api",
"to": "db",
"label": "query"
},
{
"from": "api",
"to": "worker",
"label": "enqueue"
},
{
"from": "worker",
"to": "db",
"label": "legacy path"
}
]
}
},
"nodes": [
{
"id": "client",
"label": "Client",
"shape": "ellipse",
"role": "person"
},
{
"id": "api",
"label": "Core API",
"shape": "rectangle",
"role": "service",
"parentId": "backend"
},
{
"id": "redis",
"label": "Redis",
"shape": "rectangle",
"role": "database",
"parentId": "cache"
},
{
"id": "db",
"label": "Database",
"shape": "rectangle",
"role": "database",
"parentId": "backend"
},
{
"id": "worker",
"label": "Worker",
"shape": "rectangle",
"role": "service",
"parentId": "backend"
}
],
"connections": [
{
"id": "df-edge-1",
"from": "client",
"to": "api",
"role": "relationship",
"label": "request",
"labelId": "df-label-1"
},
{
"id": "df-edge-2",
"from": "api",
"to": "redis",
"role": "relationship",
"label": "query",
"labelId": "df-label-2"
},
{
"id": "df-edge-3",
"from": "api",
"to": "worker",
"role": "relationship",
"label": "enqueue",
"labelId": "df-label-3"
},
{
"id": "redis-db",
"from": "redis",
"to": "db",
"role": "relationship"
}
],
"groups": [
{
"id": "backend",
"label": "Backend services",
"padding": 32
},
{
"id": "cache",
"label": "Cache",
"parentId": "backend",
"padding": 32
}
],
"annotations": [
{
"id": "df-title"
}
]
},
"view": {
"id": "main",
"pins": [
{
"id": "api",
"x": 573,
"y": 207
},
{
"id": "db",
"x": 1408,
"y": 207
}
],
"constraints": [],
"intentionalOverlaps": []
}
}
```
</details>
[Before image](examples/semantic-before.svg) · [Before source](examples/semantic-before.drawfather) · [Atomic edit batch](examples/semantic-edits.json)
### Choose your sketch character
Compare tidy, handwritten, and loose strokes with square, soft, and round corners. The first three rows use seed 42; the last row shows different seeds with the same handwritten style. Randomness is reproducible, and style can be chosen per drawing or per element.

**Try asking your agent:** Draw a comparison of all three sketch levels with square and rounded boxes.
[Editable source](examples/sketch-levels.drawfather) · [SVG image](examples/sketch-levels.svg)
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"revision": 0,
"title": "Sketch levels and rounded boxes",
"seed": 42,
"canvas": {
"width": 1150,
"height": 950,
"background": "#fffdf7"
},
"version": 1,
"elements": [
{
"id": "title",
"x": 48,
"y": 25,
"width": 1050,
"height": 70,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "How hand-drawn should it feel?",
"fontSize": 36,
"type": "text",
"align": "left"
},
{
"id": "subtitle",
"x": 48,
"y": 95,
"width": 1050,
"height": 60,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Three sketch levels. Three corner styles. Always reproducible.",
"fontSize": 20,
"type": "text",
"align": "left"
},
{
"id": "label-tidy",
"x": 42,
"y": 185,
"width": 190,
"height": 130,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Tidy",
"fontSize": 25,
"type": "text",
"align": "left"
},
{
"id": "tidy-0",
"x": 250,
"y": 185,
"width": 240,
"height": 130,
"sketch": "tidy",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 42,
"text": "Square corners",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "tidy-1",
"x": 540,
"y": 185,
"width": 240,
"height": 130,
"sketch": "tidy",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#d3e1f5",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 42,
"text": "Soft corners",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.3
},
{
"id": "tidy-2",
"x": 830,
"y": 185,
"width": 240,
"height": 130,
"sketch": "tidy",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#c6e6d5",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 42,
"text": "Round corners",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.7
},
{
"id": "label-handwritten",
"x": 42,
"y": 375,
"width": 190,
"height": 130,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Handwritten",
"fontSize": 25,
"type": "text",
"align": "left"
},
{
"id": "handwritten-0",
"x": 250,
"y": 375,
"width": 240,
"height": 130,
"sketch": "handwritten",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 42,
"text": "Square corners",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "handwritten-1",
"x": 540,
"y": 375,
"width": 240,
"height": 130,
"sketch": "handwritten",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#d3e1f5",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 42,
"text": "Soft corners",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.3
},
{
"id": "handwritten-2",
"x": 830,
"y": 375,
"width": 240,
"height": 130,
"sketch": "handwritten",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#c6e6d5",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 42,
"text": "Round corners",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.7
},
{
"id": "label-loose",
"x": 42,
"y": 565,
"width": 190,
"height": 130,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Loose",
"fontSize": 25,
"type": "text",
"align": "left"
},
{
"id": "loose-0",
"x": 250,
"y": 565,
"width": 240,
"height": 130,
"sketch": "loose",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 42,
"text": "Square corners",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "loose-1",
"x": 540,
"y": 565,
"width": 240,
"height": 130,
"sketch": "loose",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#d3e1f5",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 42,
"text": "Soft corners",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.3
},
{
"id": "loose-2",
"x": 830,
"y": 565,
"width": 240,
"height": 130,
"sketch": "loose",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#c6e6d5",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 42,
"text": "Round corners",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.7
},
{
"id": "label-variation",
"x": 42,
"y": 755,
"width": 190,
"height": 130,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "New seed",
"fontSize": 25,
"type": "text",
"align": "left"
},
{
"id": "variation-17",
"x": 250,
"y": 755,
"width": 240,
"height": 130,
"sketch": "handwritten",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 17,
"text": "Seed 17",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.3
},
{
"id": "variation-42",
"x": 540,
"y": 755,
"width": 240,
"height": 130,
"sketch": "handwritten",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 42,
"text": "Seed 42",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.3
},
{
"id": "variation-99",
"x": 830,
"y": 755,
"width": 240,
"height": 130,
"sketch": "handwritten",
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"seed": 99,
"text": "Seed 99",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.3
}
]
}
```
</details>
### Automatic process layout
Four labels become a complete hand-drawn flow. Longer flows wrap into rows automatically; choose vertical or horizontal when needed.

**Try asking your agent:** Use the process template to draw signup through first value.
[Editable source](examples/auto-process.drawfather) · [SVG image](examples/auto-process.svg)
**Coordinate-free input** ([JSON file](examples/compositions/auto-process.json)):
```json
{
"template": "process",
"title": "From signup to first value",
"steps": ["Sign up", "Verify email", "Create workspace", "First useful action"]
}
```
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"revision": 0,
"title": "From signup to first value",
"seed": 42,
"sketch": "handwritten",
"canvas": {
"width": 1264,
"height": 341,
"background": "#fffdf7"
},
"version": 2,
"elements": [
{
"id": "df-title",
"x": 64,
"y": 64,
"width": 1136,
"height": 73,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "From signup to first value",
"fontSize": 36,
"type": "text",
"align": "left"
},
{
"id": "step-1",
"x": 76.5,
"y": 181,
"width": 184,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Sign up",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "step-2",
"x": 385.5,
"y": 181,
"width": 184,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#d3e1f5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Verify email",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "step-3",
"x": 687,
"y": 181,
"width": 199,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#c6e6d5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Create workspace",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "step-4",
"x": 991,
"y": 181,
"width": 209,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#f5c8b6",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "First useful action",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "df-edge-1",
"type": "arrow",
"from": {
"elementId": "step-1",
"anchor": "right"
},
"to": {
"elementId": "step-2",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "df-edge-2",
"type": "arrow",
"from": {
"elementId": "step-2",
"anchor": "right"
},
"to": {
"elementId": "step-3",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "df-edge-3",
"type": "arrow",
"from": {
"elementId": "step-3",
"anchor": "right"
},
"to": {
"elementId": "step-4",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
}
],
"model": {
"kind": "process",
"direction": "auto",
"origin": {
"source": "composition",
"composition": {
"title": "From signup to first value",
"seed": 42,
"sketch": "handwritten",
"roundness": 0.25,
"template": "process",
"direction": "auto",
"steps": [
"Sign up",
"Verify email",
"Create workspace",
"First useful action"
]
}
},
"nodes": [
{
"id": "step-1",
"label": "Sign up",
"shape": "rectangle",
"role": "step"
},
{
"id": "step-2",
"label": "Verify email",
"shape": "rectangle",
"role": "step"
},
{
"id": "step-3",
"label": "Create workspace",
"shape": "rectangle",
"role": "step"
},
{
"id": "step-4",
"label": "First useful action",
"shape": "rectangle",
"role": "step"
}
],
"connections": [
{
"id": "df-edge-1",
"from": "step-1",
"to": "step-2",
"role": "flow"
},
{
"id": "df-edge-2",
"from": "step-2",
"to": "step-3",
"role": "flow"
},
{
"id": "df-edge-3",
"from": "step-3",
"to": "step-4",
"role": "flow"
}
],
"groups": [],
"annotations": [
{
"id": "df-title"
}
]
},
"view": {
"id": "main",
"pins": [],
"constraints": [],
"intentionalOverlaps": []
}
}
```
</details>
### Automatic feedback loop
A compact Build-Measure-Learn request becomes a circular composition with curved arrows and measured lettering.

**Try asking your agent:** Use the cycle template to sketch Build, Measure, Learn with a short explanation of each step.
[Editable source](examples/auto-cycle.drawfather) · [SVG image](examples/auto-cycle.svg)
**Coordinate-free input** ([JSON file](examples/compositions/auto-cycle.json)):
```json
{
"template": "cycle",
"title": "Build, Measure, Learn",
"steps": [
{ "id": "build", "label": "Build", "detail": "Ship a small MVP" },
{ "id": "measure", "label": "Measure", "detail": "Observe behavior" },
{ "id": "learn", "label": "Learn", "detail": "Choose the next test" }
]
}
```
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"revision": 0,
"title": "Build, Measure, Learn",
"seed": 42,
"sketch": "handwritten",
"canvas": {
"width": 980,
"height": 954,
"background": "#fffdf7"
},
"version": 2,
"elements": [
{
"id": "df-title",
"x": 64,
"y": 64,
"width": 852,
"height": 73,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Build, Measure, Learn",
"fontSize": 36,
"type": "text",
"align": "left"
},
{
"id": "build",
"x": 387.5,
"y": 281,
"width": 205,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Build\nShip a small MVP",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "measure",
"x": 571.7798509084399,
"y": 590.6557851430068,
"width": 194,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#d3e1f5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Measure\nObserve behavior",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "learn",
"x": 191.72014909156016,
"y": 590.6557851430068,
"width": 239,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#c6e6d5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Learn\nChoose the next test",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "df-edge-1",
"type": "arrow",
"from": {
"elementId": "build",
"anchor": "right"
},
"to": {
"elementId": "measure",
"anchor": "top"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": -90,
"text": "",
"fontSize": 24
},
{
"id": "df-edge-2",
"type": "arrow",
"from": {
"elementId": "measure",
"anchor": "bottom"
},
"to": {
"elementId": "learn",
"anchor": "bottom"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": -90,
"text": "",
"fontSize": 24
},
{
"id": "df-edge-3",
"type": "arrow",
"from": {
"elementId": "learn",
"anchor": "top"
},
"to": {
"elementId": "build",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": -90,
"text": "",
"fontSize": 24
}
],
"model": {
"kind": "cycle",
"direction": "auto",
"origin": {
"source": "composition",
"composition": {
"title": "Build, Measure, Learn",
"seed": 42,
"sketch": "handwritten",
"roundness": 0.25,
"template": "cycle",
"steps": [
{
"id": "build",
"label": "Build",
"detail": "Ship a small MVP",
"shape": "rectangle"
},
{
"id": "measure",
"label": "Measure",
"detail": "Observe behavior",
"shape": "rectangle"
},
{
"id": "learn",
"label": "Learn",
"detail": "Choose the next test",
"shape": "rectangle"
}
]
}
},
"nodes": [
{
"id": "build",
"label": "Build",
"detail": "Ship a small MVP",
"shape": "rectangle",
"role": "step"
},
{
"id": "measure",
"label": "Measure",
"detail": "Observe behavior",
"shape": "rectangle",
"role": "step"
},
{
"id": "learn",
"label": "Learn",
"detail": "Choose the next test",
"shape": "rectangle",
"role": "step"
}
],
"connections": [
{
"id": "df-edge-1",
"from": "build",
"to": "measure",
"role": "flow"
},
{
"id": "df-edge-2",
"from": "measure",
"to": "learn",
"role": "flow"
},
{
"id": "df-edge-3",
"from": "learn",
"to": "build",
"role": "flow"
}
],
"groups": [],
"annotations": [
{
"id": "df-title"
}
]
},
"view": {
"id": "main",
"pins": [],
"constraints": [],
"intentionalOverlaps": []
}
}
```
</details>
Framework: [Eric Ries, The Lean Startup methodology](https://theleanstartup.com/principles).
### Automatic startup architecture
Nodes and connections become a layered system diagram. ELK positions the nodes, routes connectors, and places their labels; DrawFather draws rounded hand-sketched routes.

**Try asking your agent:** Use the architecture template for a web app, API, database, and background worker, with a customer entering through the app.
[Editable source](examples/auto-architecture.drawfather) · [SVG image](examples/auto-architecture.svg)
**Coordinate-free input** ([JSON file](examples/compositions/auto-architecture.json)):
```json
{
"template": "architecture",
"title": "Our startup in one sketch",
"nodes": [
{ "id": "customer", "label": "Customer", "shape": "ellipse" },
{ "id": "app", "label": "Web app", "detail": "The product interface" },
{ "id": "api", "label": "API", "detail": "Business logic" },
{ "id": "db", "label": "Database", "detail": "Product data" },
{ "id": "jobs", "label": "Worker", "detail": "Background jobs" }
],
"connections": [
{ "from": "customer", "to": "app", "label": "visit" },
{ "from": "app", "to": "api", "label": "request" },
{ "from": "api", "to": "db", "label": "read / write" },
{ "from": "api", "to": "jobs", "label": "enqueue" }
]
}
```
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"revision": 0,
"title": "Our startup in one sketch",
"seed": 42,
"sketch": "handwritten",
"canvas": {
"width": 1718,
"height": 554,
"background": "#fffdf7"
},
"version": 2,
"elements": [
{
"id": "df-title",
"x": 64,
"y": 64,
"width": 1590,
"height": 73,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Our startup in one sketch",
"fontSize": 36,
"type": "text",
"align": "left"
},
{
"id": "customer",
"x": 88,
"y": 207,
"width": 271,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Customer",
"fontSize": 24,
"type": "ellipse"
},
{
"id": "app",
"x": 551,
"y": 207,
"width": 242,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#d3e1f5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Web app\nThe product interface",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "api",
"x": 1007,
"y": 207,
"width": 184,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#c6e6d5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "API\nBusiness logic",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "db",
"x": 1441,
"y": 207,
"width": 184,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#f5c8b6",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Database\nProduct data",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "jobs",
"x": 1441,
"y": 367,
"width": 189,
"height": 96,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#e2d4ef",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Worker\nBackground jobs",
"fontSize": 24,
"type": "rectangle",
"roundness": 0.25
},
{
"id": "df-edge-1",
"type": "arrow",
"from": {
"elementId": "customer",
"anchor": "right"
},
"to": {
"elementId": "app",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "df-label-1",
"x": 423,
"y": 258,
"width": 64,
"height": 47,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "visit",
"fontSize": 18,
"type": "text",
"align": "center"
},
{
"id": "df-edge-2",
"type": "arrow",
"from": {
"elementId": "app",
"anchor": "right"
},
"to": {
"elementId": "api",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "df-label-2",
"x": 857,
"y": 258,
"width": 86,
"height": 47,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "request",
"fontSize": 18,
"type": "text",
"align": "center"
},
{
"id": "df-edge-3",
"type": "arrow",
"from": {
"elementId": "api",
"anchor": "right"
},
"to": {
"elementId": "db",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "df-label-3",
"x": 1255,
"y": 205,
"width": 122,
"height": 47,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "read / write",
"fontSize": 18,
"type": "text",
"align": "center"
},
{
"id": "df-edge-4",
"type": "arrow",
"from": {
"elementId": "api",
"anchor": "right"
},
"to": {
"elementId": "jobs",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"waypoints": [
[
1223,
255
],
[
1223,
415
]
],
"text": "",
"fontSize": 24
},
{
"id": "df-label-4",
"x": 1270,
"y": 418,
"width": 92,
"height": 47,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "enqueue",
"fontSize": 18,
"type": "text",
"align": "center"
}
],
"model": {
"kind": "architecture",
"direction": "auto",
"origin": {
"source": "composition",
"composition": {
"title": "Our startup in one sketch",
"seed": 42,
"sketch": "handwritten",
"roundness": 0.25,
"template": "architecture",
"direction": "auto",
"nodes": [
{
"id": "customer",
"label": "Customer",
"shape": "ellipse"
},
{
"id": "app",
"label": "Web app",
"detail": "The product interface",
"shape": "rectangle"
},
{
"id": "api",
"label": "API",
"detail": "Business logic",
"shape": "rectangle"
},
{
"id": "db",
"label": "Database",
"detail": "Product data",
"shape": "rectangle"
},
{
"id": "jobs",
"label": "Worker",
"detail": "Background jobs",
"shape": "rectangle"
}
],
"connections": [
{
"from": "customer",
"to": "app",
"label": "visit"
},
{
"from": "app",
"to": "api",
"label": "request"
},
{
"from": "api",
"to": "db",
"label": "read / write"
},
{
"from": "api",
"to": "jobs",
"label": "enqueue"
}
]
}
},
"nodes": [
{
"id": "customer",
"label": "Customer",
"shape": "ellipse",
"role": "service"
},
{
"id": "app",
"label": "Web app",
"detail": "The product interface",
"shape": "rectangle",
"role": "service"
},
{
"id": "api",
"label": "API",
"detail": "Business logic",
"shape": "rectangle",
"role": "service"
},
{
"id": "db",
"label": "Database",
"detail": "Product data",
"shape": "rectangle",
"role": "service"
},
{
"id": "jobs",
"label": "Worker",
"detail": "Background jobs",
"shape": "rectangle",
"role": "service"
}
],
"connections": [
{
"id": "df-edge-1",
"from": "customer",
"to": "app",
"role": "relationship",
"label": "visit",
"labelId": "df-label-1"
},
{
"id": "df-edge-2",
"from": "app",
"to": "api",
"role": "relationship",
"label": "request",
"labelId": "df-label-2"
},
{
"id": "df-edge-3",
"from": "api",
"to": "db",
"role": "relationship",
"label": "read / write",
"labelId": "df-label-3"
},
{
"id": "df-edge-4",
"from": "api",
"to": "jobs",
"role": "relationship",
"label": "enqueue",
"labelId": "df-label-4"
}
],
"groups": [],
"annotations": [
{
"id": "df-title"
}
]
},
"view": {
"id": "main",
"pins": [],
"constraints": [],
"intentionalOverlaps": []
}
}
```
</details>
### Lean Startup feedback loop
Build a small MVP, measure customer behavior, and use the learning to choose the next experiment.

**Try asking your agent:** Draw the Lean Startup Build-Measure-Learn feedback loop in a hand-drawn style.
[Editable source](examples/lean-startup.drawfather) · [SVG image](examples/lean-startup.svg)
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"version": 1,
"title": "Lean startup: Build, Measure, Learn",
"seed": 17,
"canvas": {"width": 1120, "height": 660, "background": "#fffdf7"},
"elements": [
{"id": "title", "type": "text", "x": 48, "y": 22, "width": 1024, "height": 72, "fontSize": 38, "text": "Lean startup: Build, Measure, Learn", "align": "left"},
{"id": "subtitle", "type": "text", "x": 48, "y": 98, "width": 1024, "height": 48, "fontSize": 21, "text": "Turn one assumption into one small experiment.", "align": "left"},
{"id": "build", "type": "rectangle", "x": 440, "y": 175, "width": 240, "height": 130, "text": "Build\nShip a small MVP", "fontSize": 24, "fill": "#ffe3a0"},
{"id": "measure", "type": "rectangle", "x": 770, "y": 385, "width": 240, "height": 130, "text": "Measure\nObserve behavior", "fontSize": 24, "fill": "#d3e1f5"},
{"id": "learn", "type": "rectangle", "x": 110, "y": 385, "width": 240, "height": 130, "text": "Learn\nPivot or persevere", "fontSize": 24, "fill": "#c6e6d5"},
{"id": "build-to-measure", "type": "arrow", "from": {"elementId": "build", "anchor": "right"}, "to": {"elementId": "measure", "anchor": "top"}, "text": "data", "curve": -55},
{"id": "measure-to-learn", "type": "arrow", "from": {"elementId": "measure", "anchor": "auto"}, "to": {"elementId": "learn", "anchor": "auto"}, "text": "insight", "curve": -55},
{"id": "learn-to-build", "type": "arrow", "from": {"elementId": "learn", "anchor": "top"}, "to": {"elementId": "build", "anchor": "left"}, "curve": -55, "text": "next test"},
{"id": "footer", "type": "text", "x": 60, "y": 575, "width": 1000, "height": 48, "fontSize": 23, "text": "Repeat the loop with what you learned.", "align": "center"}
]
}
```
</details>
Framework: [Eric Ries, The Lean Startup methodology](https://theleanstartup.com/principles).
### AARRR startup metrics
A familiar startup growth framework: Acquisition, Activation, Retention, Referral, and Revenue. The questions are illustrative, not benchmark data.

**Try asking your agent:** Draw an AARRR startup metrics funnel with one question for each stage.
[Editable source](examples/aarrr-metrics.drawfather) · [SVG image](examples/aarrr-metrics.svg)
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"version": 1,
"title": "AARRR: startup pirate metrics",
"seed": 23,
"canvas": {"width": 1120, "height": 820, "background": "#fffdf7"},
"elements": [
{"id": "title", "type": "text", "x": 48, "y": 22, "width": 1024, "height": 72, "fontSize": 38, "text": "AARRR: startup pirate metrics", "align": "left"},
{"id": "subtitle", "type": "text", "x": 48, "y": 98, "width": 1024, "height": 48, "fontSize": 21, "text": "Five lenses on the customer journey.", "align": "left"},
{"id": "acquisition", "type": "rectangle", "x": 80.0, "y": 170, "width": 560, "height": 80, "text": "01 Acquisition", "fontSize": 26, "fill": "#ffe3a0"},
{"id": "acquisition-question", "type": "text", "x": 710, "y": 178, "width": 350, "height": 64, "fontSize": 22, "text": "How do people find you?", "align": "left"},
{"id": "activation", "type": "rectangle", "x": 120.0, "y": 290, "width": 480, "height": 80, "text": "02 Activation", "fontSize": 26, "fill": "#d3e1f5"},
{"id": "activation-question", "type": "text", "x": 710, "y": 298, "width": 350, "height": 64, "fontSize": 22, "text": "When do they first get value?", "align": "left"},
{"id": "stage-1", "type": "arrow", "from": {"elementId": "acquisition", "anchor": "auto"}, "to": {"elementId": "activation", "anchor": "auto"}},
{"id": "retention", "type": "rectangle", "x": 160.0, "y": 410, "width": 400, "height": 80, "text": "03 Retention", "fontSize": 26, "fill": "#c6e6d5"},
{"id": "retention-question", "type": "text", "x": 710, "y": 418, "width": 350, "height": 64, "fontSize": 22, "text": "Do they come back?", "align": "left"},
{"id": "stage-2", "type": "arrow", "from": {"elementId": "activation", "anchor": "auto"}, "to": {"elementId": "retention", "anchor": "auto"}},
{"id": "referral", "type": "rectangle", "x": 200.0, "y": 530, "width": 320, "height": 80, "text": "04 Referral", "fontSize": 26, "fill": "#e2d4ef"},
{"id": "referral-question", "type": "text", "x": 710, "y": 538, "width": 350, "height": 64, "fontSize": 22, "text": "Do they bring other people?", "align": "left"},
{"id": "stage-3", "type": "arrow", "from": {"elementId": "retention", "anchor": "auto"}, "to": {"elementId": "referral", "anchor": "auto"}},
{"id": "revenue", "type": "rectangle", "x": 240.0, "y": 650, "width": 240, "height": 80, "text": "05 Revenue", "fontSize": 26, "fill": "#f5c8b6"},
{"id": "revenue-question", "type": "text", "x": 710, "y": 658, "width": 350, "height": 64, "fontSize": 22, "text": "Do they pay for that value?", "align": "left"},
{"id": "stage-4", "type": "arrow", "from": {"elementId": "referral", "anchor": "auto"}, "to": {"elementId": "revenue", "anchor": "auto"}},
{"id": "footer", "type": "text", "x": 70, "y": 755, "width": 980, "height": 45, "fontSize": 23, "text": "Choose a measurable event for each stage.", "align": "center"}
]
}
```
</details>
Framework: [Dave McClure, Startup Metrics for Pirates](https://www.slideshare.net/slideshow/startup-metrics-for-pirates-long-version/89026).
### SaaS MVP architecture
An illustrative startup architecture connecting a web app, API, database, background worker, and email provider.

**Try asking your agent:** Sketch a simple SaaS MVP architecture, including background jobs and email notifications.
[Editable source](examples/saas-architecture.drawfather) · [SVG image](examples/saas-architecture.svg)
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"revision": 1,
"title": "A SaaS MVP architecture",
"seed": 31,
"canvas": {
"width": 1240,
"height": 760,
"background": "#fffdf7"
},
"version": 1,
"elements": [
{
"id": "title",
"x": 48,
"y": 22,
"width": 1144,
"height": 72,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "A SaaS MVP architecture",
"fontSize": 38,
"type": "text",
"align": "left"
},
{
"id": "subtitle",
"x": 48,
"y": 98,
"width": 1144,
"height": 48,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "From a customer request to background work.",
"fontSize": 21,
"type": "text",
"align": "left"
},
{
"id": "browser",
"x": 60,
"y": 220,
"width": 220,
"height": 120,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Customer\nBrowser",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "app",
"x": 425,
"y": 220,
"width": 240,
"height": 120,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#d3e1f5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Web app\nProduct interface",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "api",
"x": 825,
"y": 220,
"width": 240,
"height": 120,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#c6e6d5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "API\nBusiness logic",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "database",
"x": 825,
"y": 510,
"width": 240,
"height": 120,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#e2d4ef",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Database\nProduct data",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "worker",
"x": 425,
"y": 510,
"width": 240,
"height": 120,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#f5c8b6",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Worker\nAsync jobs",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "email",
"x": 60,
"y": 510,
"width": 220,
"height": 120,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Email provider\nNotifications",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "browser-to-app",
"type": "arrow",
"from": {
"elementId": "browser",
"anchor": "auto"
},
"to": {
"elementId": "app",
"anchor": "auto"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "HTTPS",
"fontSize": 24
},
{
"id": "app-to-api",
"type": "arrow",
"from": {
"elementId": "app",
"anchor": "right"
},
"to": {
"elementId": "api",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"waypoints": [
[
699,
280
],
[
791,
280
]
],
"text": "",
"fontSize": 24
},
{
"id": "api-to-database",
"type": "arrow",
"from": {
"elementId": "api",
"anchor": "auto"
},
"to": {
"elementId": "database",
"anchor": "auto"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "api-to-worker",
"type": "arrow",
"from": {
"elementId": "api",
"anchor": "left"
},
"to": {
"elementId": "worker",
"anchor": "top"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "worker-to-email",
"type": "arrow",
"from": {
"elementId": "worker",
"anchor": "auto"
},
"to": {
"elementId": "email",
"anchor": "auto"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "send",
"fontSize": 24
},
{
"id": "api-to-database-label",
"x": 965,
"y": 385,
"width": 220,
"height": 55,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "read / write",
"fontSize": 22,
"type": "text",
"align": "center"
},
{
"id": "api-to-worker-label",
"x": 500,
"y": 370,
"width": 160,
"height": 48,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "enqueue",
"fontSize": 22,
"type": "text",
"align": "center"
},
{
"id": "footer",
"x": 60,
"y": 680,
"width": 1120,
"height": 48,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Illustrative architecture. Adapt it to your product.",
"fontSize": 22,
"type": "text",
"align": "center"
},
{
"id": "df-label-e002cbb973d94289cd27",
"x": 702,
"y": 197,
"width": 86,
"height": 47,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "request",
"fontSize": 18,
"type": "text",
"align": "center"
}
]
}
```
</details>
### CI/CD delivery pipeline
A common commit-to-production process with a test failure path that returns work to the build stage.

**Try asking your agent:** Draw a CI/CD pipeline from commit to production, including a fix-and-retry path when tests fail.
[Editable source](examples/cicd-pipeline.drawfather) · [SVG image](examples/cicd-pipeline.svg)
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"revision": 1,
"title": "CI/CD: from commit to production",
"seed": 43,
"canvas": {
"width": 1280,
"height": 680,
"background": "#fffdf7"
},
"version": 1,
"elements": [
{
"id": "title",
"x": 48,
"y": 22,
"width": 1184,
"height": 72,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "CI/CD: from commit to production",
"fontSize": 38,
"type": "text",
"align": "left"
},
{
"id": "subtitle",
"x": 48,
"y": 98,
"width": 1184,
"height": 48,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "A delivery pipeline with an explicit quality gate.",
"fontSize": 21,
"type": "text",
"align": "left"
},
{
"id": "commit",
"x": 50,
"y": 250,
"width": 170,
"height": 130,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Commit\nPush changes",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "build",
"x": 300,
"y": 250,
"width": 170,
"height": 130,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#d3e1f5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Build\nCreate artifact",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "test",
"x": 550,
"y": 250,
"width": 170,
"height": 130,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#e2d4ef",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Test\nCheck behavior",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "stage",
"x": 800,
"y": 250,
"width": 170,
"height": 130,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#f5c8b6",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Stage\nPreview release",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "production",
"x": 1050,
"y": 250,
"width": 170,
"height": 130,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#c6e6d5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Production\nRelease",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "fix",
"x": 535,
"y": 500,
"width": 200,
"height": 80,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#f5c8b6",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Fix & retry",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "commit-to-build",
"type": "arrow",
"from": {
"elementId": "commit",
"anchor": "auto"
},
"to": {
"elementId": "build",
"anchor": "auto"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "build-to-test",
"type": "arrow",
"from": {
"elementId": "build",
"anchor": "auto"
},
"to": {
"elementId": "test",
"anchor": "auto"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "test-to-stage",
"type": "arrow",
"from": {
"elementId": "test",
"anchor": "right"
},
"to": {
"elementId": "stage",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"waypoints": [
[
754,
315
],
[
766,
315
]
],
"text": "",
"fontSize": 24
},
{
"id": "stage-to-production",
"type": "arrow",
"from": {
"elementId": "stage",
"anchor": "auto"
},
"to": {
"elementId": "production",
"anchor": "auto"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "test-to-fix",
"type": "arrow",
"from": {
"elementId": "test",
"anchor": "bottom"
},
"to": {
"elementId": "fix",
"anchor": "top"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "test-to-fix-label",
"x": 650,
"y": 410,
"width": 100,
"height": 48,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "fail",
"fontSize": 22,
"type": "text",
"align": "center"
},
{
"id": "fix-to-build",
"type": "arrow",
"from": {
"elementId": "fix",
"anchor": "left"
},
"to": {
"elementId": "build",
"anchor": "bottom"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": -70,
"text": "retry",
"fontSize": 24
},
{
"id": "footer",
"x": 50,
"y": 615,
"width": 1180,
"height": 47,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Example stages; your team can add its own checks and approvals.",
"fontSize": 21,
"type": "text",
"align": "center"
},
{
"id": "df-label-c46d198461a1446bd777",
"x": 720,
"y": 232,
"width": 80,
"height": 47,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "pass",
"fontSize": 18,
"type": "text",
"align": "center"
}
]
}
```
</details>
### Customer onboarding decision tree
A signup flow with an email-verification decision, a resend-and-retry path, and the first useful product action.

**Try asking your agent:** Draw customer onboarding from signup to first value, with an email-verification decision and retry path.
[Editable source](examples/onboarding-flow.drawfather) · [SVG image](examples/onboarding-flow.svg)
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"revision": 1,
"title": "Customer onboarding: the first useful action",
"seed": 59,
"canvas": {
"width": 1120,
"height": 790,
"background": "#fffdf7"
},
"version": 1,
"elements": [
{
"id": "title",
"x": 48,
"y": 22,
"width": 1024,
"height": 72,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Customer onboarding: the first useful action",
"fontSize": 38,
"type": "text",
"align": "left"
},
{
"id": "subtitle",
"x": 48,
"y": 98,
"width": 1024,
"height": 48,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "A decision tree with a recovery path.",
"fontSize": 21,
"type": "text",
"align": "left"
},
{
"id": "signup",
"x": 60,
"y": 200,
"width": 200,
"height": 100,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#ffe3a0",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Sign up",
"fontSize": 24,
"type": "ellipse"
},
{
"id": "verified",
"x": 400,
"y": 165,
"width": 260,
"height": 220,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#d3e1f5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Email\nverified?",
"fontSize": 24,
"type": "diamond"
},
{
"id": "workspace",
"x": 820,
"y": 200,
"width": 220,
"height": 100,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#c6e6d5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Create workspace",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "resend",
"x": 420,
"y": 520,
"width": 220,
"height": 110,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#f5c8b6",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Resend email",
"fontSize": 24,
"type": "rectangle",
"roundness": 0
},
{
"id": "value",
"x": 820,
"y": 520,
"width": 220,
"height": 126,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "#c6e6d5",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "First useful\naction",
"fontSize": 24,
"type": "ellipse"
},
{
"id": "signup-to-verified",
"type": "arrow",
"from": {
"elementId": "signup",
"anchor": "auto"
},
"to": {
"elementId": "verified",
"anchor": "auto"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "verified-to-workspace",
"type": "arrow",
"from": {
"elementId": "verified",
"anchor": "right"
},
"to": {
"elementId": "workspace",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"waypoints": [
[
694,
275
],
[
786,
275
],
[
786,
250
]
],
"text": "",
"fontSize": 24
},
{
"id": "verified-to-resend",
"type": "arrow",
"from": {
"elementId": "verified",
"anchor": "bottom"
},
"to": {
"elementId": "resend",
"anchor": "top"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"text": "",
"fontSize": 24
},
{
"id": "resend-to-verified",
"type": "arrow",
"from": {
"elementId": "resend",
"anchor": "left"
},
"to": {
"elementId": "verified",
"anchor": "left"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": -65,
"text": "retry",
"fontSize": 24
},
{
"id": "workspace-to-value",
"type": "arrow",
"from": {
"elementId": "workspace",
"anchor": "bottom"
},
"to": {
"elementId": "value",
"anchor": "top"
},
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"curve": 0,
"waypoints": [
[
930,
334
],
[
930,
486
]
],
"text": "",
"fontSize": 24
},
{
"id": "verified-to-resend-label",
"x": 545,
"y": 415,
"width": 75,
"height": 48,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "no",
"fontSize": 22,
"type": "text",
"align": "center"
},
{
"id": "footer",
"x": 60,
"y": 705,
"width": 1000,
"height": 50,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "Guide the happy path. Make recovery clear.",
"fontSize": 23,
"type": "text",
"align": "center"
},
{
"id": "df-label-f5a4e8d6a8839b1b6852",
"x": 700,
"y": 192,
"width": 80,
"height": 47,
"stroke": "#252525",
"strokeWidth": 2,
"fill": "transparent",
"fillStyle": "hachure",
"roughness": 1.2,
"text": "yes",
"fontSize": 18,
"type": "text",
"align": "center"
}
]
}
```
</details>
### Kanban board
A three-column work board showing startup tasks moving from To do to In progress to Done.

**Try asking your agent:** Draw a hand-sketched Kanban board for a startup MVP with To do, In progress, and Done columns.
[Editable source](examples/kanban-board.drawfather) · [SVG image](examples/kanban-board.svg)
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"version": 1,
"title": "Kanban: make the work visible",
"seed": 71,
"canvas": {"width": 1200, "height": 720, "background": "#fffdf7"},
"elements": [
{"id": "title", "type": "text", "x": 48, "y": 22, "width": 1104, "height": 72, "fontSize": 38, "text": "Kanban: make the work visible", "align": "left"},
{"id": "subtitle", "type": "text", "x": 48, "y": 98, "width": 1104, "height": 48, "fontSize": 21, "text": "One task, one column, one next step.", "align": "left"},
{"id": "todo-column", "type": "rectangle", "x": 60, "y": 190, "width": 320, "height": 420, "text": "", "fontSize": 24, "fill": "transparent"},
{"id": "doing-column", "type": "rectangle", "x": 440, "y": 190, "width": 320, "height": 420, "text": "", "fontSize": 24, "fill": "transparent"},
{"id": "done-column", "type": "rectangle", "x": 820, "y": 190, "width": 320, "height": 420, "text": "", "fontSize": 24, "fill": "transparent"},
{"id": "todo-title", "type": "text", "x": 80, "y": 210, "width": 280, "height": 65, "fontSize": 30, "text": "To do", "align": "center"},
{"id": "doing-title", "type": "text", "x": 460, "y": 210, "width": 280, "height": 65, "fontSize": 30, "text": "In progress", "align": "center"},
{"id": "done-title", "type": "text", "x": 840, "y": 210, "width": 280, "height": 65, "fontSize": 30, "text": "Done", "align": "center"},
{"id": "interview", "type": "rectangle", "x": 85, "y": 300, "width": 270, "height": 95, "text": "Interview users", "fontSize": 24, "fill": "#ffe3a0"},
{"id": "sketch", "type": "rectangle", "x": 85, "y": 430, "width": 270, "height": 95, "text": "Sketch onboarding", "fontSize": 24, "fill": "#ffe3a0"},
{"id": "mvp", "type": "rectangle", "x": 465, "y": 300, "width": 270, "height": 95, "text": "Build the MVP", "fontSize": 24, "fill": "#d3e1f5"},
{"id": "limit", "type": "text", "x": 465, "y": 445, "width": 270, "height": 60, "fontSize": 20, "text": "Limit work in progress", "align": "center"},
{"id": "problem", "type": "rectangle", "x": 845, "y": 300, "width": 270, "height": 95, "text": "Define the problem", "fontSize": 24, "fill": "#c6e6d5"},
{"id": "hypothesis", "type": "rectangle", "x": 845, "y": 430, "width": 270, "height": 95, "text": "Pick one hypothesis", "fontSize": 24, "fill": "#c6e6d5"},
{"id": "to-doing", "type": "arrow", "from": [390, 245], "to": [430, 245]},
{"id": "to-done", "type": "arrow", "from": [770, 245], "to": [810, 245]},
{"id": "footer", "type": "text", "x": 60, "y": 640, "width": 1080, "height": 45, "fontSize": 23, "text": "Move cards as work progresses.", "align": "center"}
]
}
```
</details>
### Straight and curved arrows
Compare curve: 0, curve: 60, and curve: -60. Curved arrowheads follow the arrival tangent, and labels follow the bow.

**Try asking your agent:** Draw three connections comparing a straight arrow, a downward-bowed arrow, and an upward-bowed arrow.
[Editable source](examples/curved-arrows.drawfather) · [SVG image](examples/curved-arrows.svg)
<details>
<summary>View the complete .drawfather JSON</summary>
```json
{
"type": "drawfather",
"version": 1,
"title": "Straight and curved arrows",
"seed": 83,
"canvas": {"width": 1120, "height": 810, "background": "#fffdf7"},
"elements": [
{"id": "title", "type": "text", "x": 48, "y": 22, "width": 1024, "height": 72, "fontSize": 38, "text": "A softer way to connect ideas"},
{"id": "subtitle", "type": "text", "x": 48, "y": 98, "width": 1024, "height": 48, "fontSize": 21, "text": "One optional curve field. Three different connections."},
{"id": "straight-start", "type": "rectangle", "x": 80, "y": 185, "width": 210, "height": 90, "text": "Start", "fill": "#ffe3a0"},
{"id": "straight-finish", "type": "rectangle", "x": 820, "y": 185, "width": 210, "height": 90, "text": "Finish", "fill": "#ffe3a0"},
{"id": "straight-arrow", "type": "arrow", "from": {"elementId": "straight-start"}, "to": {"elementId": "straight-finish"}, "curve": 0, "text": "curve: 0"},
{"id": "down-start", "type": "rectangle", "x": 80, "y": 355, "width": 210, "height": 90, "text": "Start", "fill": "#d3e1f5"},
{"id": "down-finish", "type": "rectangle", "x": 820, "y": 355, "width": 210, "height": 90, "text": "Finish", "fill": "#d3e1f5"},
{"id": "down-arrow", "type": "arrow", "from": {"elementId": "down-start"}, "to": {"elementId": "down-finish"}, "curve": 60, "text": "curve: 60"},
{"id": "up-start", "type": "rectangle", "x": 80, "y": 605, "width": 210, "height": 90, "text": "Start", "fill": "#c6e6d5"},
{"id": "up-finish", "type": "rectangle", "x": 820, "y": 605, "width": 210, "height": 90, "text": "Finish", "fill": "#c6e6d5"},
{"id": "up-arrow", "type": "arrow", "from": {"elementId": "up-start"}, "to": {"elementId": "up-finish"}, "curve": -60, "text": "curve: -60"},
{"id": "footer", "type": "text", "x": 60, "y": 735, "width": 1000, "height": 50, "fontSize": 22, "align": "center", "text": "A signed bend in pixels. Zero keeps the arrow straight."}
]
}
```
</details>
<!-- drawfather-gallery:end -->
## Status
An early, local-first implementation. The engine reads native v1 and v2; new compositions use semantic v2. The project has not reached a stable 1.0 release. It is not yet published to npm. Hosted accounts and billing are outside this initial release.
The [launch roadmap](ROADMAP.md) covers semantic editing, layout preservation, visual checks, documentation workflows, technical and creative diagram types, interoperability, and release verification. Every phase is required before the public product launch. Features listed there remain planned until their acceptance criteria are met.
## Run from source
Requires Node.js 22 or newer and npm.
```sh
npm ci
npm run build
node dist/cli.js --help
```
Generate a real sample and ready-to-paste client settings:
```sh
npm run demo
npm run demo -- --template cycle
npm run demo -- --template architecture
node dist/cli.js setup --directory /absolute/path/to/my-drawings
```
`demo` prints the saved source, PNG, and SVG paths and chooses a fresh filename each time. `setup` prints JSON containing the absolute Node executable, installed server path, and storage directory. Copy that JSON into a client that supports MCP stdio configuration; adapt the wrapper to your client's settings format if needed. `npm run setup` is a shortcut for the default drawing directory; npm also prints its script banner, so use `node dist/cli.js setup` when capturing only JSON. Inspect the template catalog with `node dist/cli.js templates`.
The storage folder can also be set through `DRAWFATHER_DIR`. Without either setting it defaults to `./drawings` relative to the server's working directory. Use an absolute folder path in your client configuration so your files are easy to find.
Try asking your assistant:
> Use DrawFather’s process template for a product launch: Build, Test, Ship. Show the preview and export SVG. Then rename Test to Customer validation while preserving the layout.
DrawFather runs over stdin/stdout. Running the server directly will wait for an MCP client; it does not open a web page. It sends no drawing content to an external service. Your chosen AI client may process that content according to its own settings.
## Your source file
A `.drawfather` file is plain JSON. Native v2 retains semantic nodes, connections, groups, annotations, and a constrained view alongside its rendered elements. Existing v1 files keep the simpler geometry-only structure shown below.
```json
{
"type": "drawfather",
"version": 1,
"title": "My first sketch",
"seed": 42,
"canvas": {
"width": 800,
"height": 400,
"background": "#fffdf7"
},
"elements": [
{
"id": "idea",
"type": "rectangle",
"x": 60,
"y": 140,
"width": 230,
"height": 120,
"text": "An idea",
"fill": "#ffdf80"
},
{
"id": "sketch",
"type": "ellipse",
"x": 500,
"y": 140,
"width": 230,
"height": 120,
"text": "A sketch",
"fill": "#b8e0cc"
},
{
"id": "connection",
"type": "arrow",
"from": { "elementId": "idea" },
"to": { "elementId": "sketch" },
"text": "DrawFather"
}
]
}
```
Save the source, put it in Git, share it, or edit it with any text editor or AI agent. All supported exports are available locally. To open an existing file through MCP, place it in the configured storage folder with an identifier as its filename, such as `first-sketch.drawfather`, and call `get_drawing` with `name: "first-sketch"`.
The published [v1 schema](schema/drawfather-v1.schema.json) and [v2 schema](schema/drawfather-v2.schema.json) describe both structures. Runtime validation additionally checks unique element IDs and valid arrow bindings. The format is native to DrawFather. The [interchange support matrix](schema/INTERCHANGE.md) describes implemented Mermaid, Excalidraw and draw.io conversion, explicit loss reports and optional native embedding. Use `import_drawing` / `export_interchange`; [Phase 6 evidence](PHASE6.md) records actual editor round trips.
### Elements and styles
| Type | Fields |
| --- | --- |
| `rectangle`, `ellipse`, `diamond` | `id`, `x`, `y`, `width`, `height`; optional `text`, `fontSize` |
| `text` | Same box fields; optional `align`: `left`, `center`, `right` |
| `container` (v2) | Box fields plus `padding`; managed through semantic groups |
| `arrow` | `id`, `from`, `to`; optional `curve`, `text`, `fontSize` |
| `freedraw` | `id`, `points`: at least two `[x, y]` pairs, rendered as a smooth sketch curve |
All elements accept `stroke`, `strokeWidth`, `fill`, `fillStyle`, `roughness`, `seed`, and an optional `sketch` preset override. Rectangles additionally accept `roundness`. Colors are `#rgb`, `#rrggbb`, or `transparent`. Fill styles are `hachure`, `solid`, `cross-hatch`, and `dots`. Roughness ranges from 0.5 to 3. Coordinates are pixels; box `x`/`y` always refers to the top-left, including ellipses and diamonds. Keep content inside the canvas, allowing margins for rough strokes.
Arrow endpoints accept an explicit `[x, y]` pair or `{ "elementId": "idea", "anchor": "auto" }`. Supported anchors are `auto`, `top`, `right`, `bottom`, and `left`. Bound arrows attach outside shape borders and follow moved elements. They render behind other elements. Architecture composition and affected semantic connections support obstacle routing. Self-loops remain unsupported.
### Curved arrows
Set `curve` to a signed bend in pixels. The default is `0`, which keeps the arrow straight. A positive value bends toward the right as you follow the arrow; a negative value bends toward the left. For a left-to-right arrow, `60` bows down and `-60` bows up. The magnitude is the curve's midpoint offset from the straight line between its resolved endpoints. Supported values range from `-4096` to `4096`.
```json
{
"id": "connection",
"type": "arrow",
"from": { "elementId": "idea" },
"to": { "elementId": "sketch" },
"curve": -60,
"text": "Build it"
}
```
The renderer uses a quadratic Bezier curve with a hand-drawn stroke. The arrowhead follows the tangent at the end, and its label sits outside the bow at the curve's midpoint. Bound endpoints retain their selected anchors. Leave room around curves and labels; use explicit anchors or separate text elements to refine the layout. See [the straight-and-curved comparison](examples/curved-arrows.drawfather) for complete source. Existing drawings without `curve` continue to render straight arrows.
The document seed and each element's ID produce stable sketch strokes. Explicit element seeds override that behavior. Changing another element does not randomize the whole drawing.
Lettering uses the included Kalam Latin font. Text wraps and shrinks down to 12px when needed; labels that cannot fit return an error so the agent can enlarge the box. Unsupported glyphs return an explicit error. Text is converted to outlines for consistent exports without system fonts; the editable text stays in `.drawfather`, and SVG titles preserve labels for accessibility. SVG lettering is not directly editable as SVG text.
Native files limit a drawing to 500 elements, 1,000 points per freehand element, 5 MB of source JSON, and a canvas of at most 16 million pixels with either dimension at most 8,192. The general composition templates, ten typed technical families and eight creative families provide layout; native source retains explicit coordinates. The documented subset supports Mermaid import and editable Excalidraw/draw.io handoffs. Local embedded fonts and images, supported shape rotation and vector PDF are implemented; see the [creative contract](schema/CREATIVE.md) for their boundaries. A hosted HTTP transport remains future work. Native v2 supports nested groups and layout constraints. Large compositions can exceed the canvas limits; shorten labels, change direction, or split them into smaller diagrams. Arbitrary manually positioned shapes do not receive automatic obstacle routing.
### Routed connectors
Architecture compositions use ELK to route connections and place edge labels. Native arrows support an optional `waypoints` array of 1–32 absolute interior `[x, y]` positions. The renderer rounds the corners of that route. Omit `curve`, or leave it at zero, when using waypoints. Endpoints can still bind to shapes; interior route points stay at their saved coordinates. Semantic edits and supported v2 manual updates refresh affected routes. V1 raw geometry keeps its saved interior points; migrate it before using semantic editing.
## MCP tools
| Tool | Purpose |
| --- | --- |
| `list_templates` | Discover process, cycle, and architecture inputs |
| `compose_technical` | Compose ten typed technical families with symbols, views, callouts and legends |
| `get_capabilities` | Concise discovery of templates, primitives/roles, styles, operations, and examples |
| `compose_drawing` | Automatically lay out a diagram and return compact saved-state metadata |
| `get_format` | JSON schema plus a minimal source example |
| `list_drawings` | Names of saved `.drawfather` files |
| `create_drawing` | Save a new drawing; never overwrite an existing one |
| `get_drawing` | Summary, paged elements/graph, or explicit full source |
| `edit_drawing` | Atomic semantic edits, pins, groups, constraints, and saved-model reflow |
| `patch_drawing` | Small label/detail/style patches without resending geometry |
| `migrate_drawing` | Upgrade v1 to v2 with an exact original backup |
| `drawing_history` | List local history and backups; undo, redo, or restore a commit |
| `check_drawing` | Structured visual diagnostics and corrections without changing source |
| `repair_drawing` | Preview a bounded repair, then apply the exact reviewed plan as one revision |
| `update_drawing` | Replace or remove elements, update title/seed/sketch/canvas, check revision |
| `preview_drawing` | Read-only PNG with size, region, selection, and background controls |
| `compose_creative` | Eight typed creative/planning families; reuse saved templates and style/component libraries |
| `edit_creative` | Branch, schedule, journey, board, annotation and panel edits with stable IDs |
| `set_presentation` | Embed local images/fonts; apply brand presets, glyph fallback and text alternatives |
| `export_drawing` | Stable/revisioned SVG, PNG, JPG, WebP, vector PDF with theme/scale settings and optional metadata |
| `project_diagrams` | Manifest-based rendering, doc links/sync, freshness, affected diagrams, review diffs and acknowledgements |
Mutations default to compact structured metadata. Request `includeSource: true` for full JSON and `preview: true` or preview options for a PNG. `get_drawing` defaults to a summary; its `source` view retrieves complete source. Exports write full-resolution files and default to metadata-only delivery. See [agent API options](schema/AGENT_API.md).
Native schemas are also available as MCP resources at `drawfather://schema/v1` and `drawfather://schema/v2`. `get_format` returns the current schema, a v2 example, and the legacy schema.
Creation starts at revision 1. An update requires `expectedRevision`, obtained by reading the drawing first. `upsert` contains complete elements, replacing existing elements by ID or appending new ones. `remove` contains element IDs. For v1 raw updates, remove a shape and its bound arrows together. V2 reconciliation removes a deleted semantic node's connections and owned labels; pins still require explicit unpin before removal. Imported files without a revision start at 0, which is accepted as `expectedRevision`.
```json
{
"name": "first-sketch",
"expectedRevision": 1,
"upsert": [
{
"id": "idea",
"type": "rectangle",
"x": 60,
"y": 140,
"width": 230,
"height": 120,
"text": "A better idea",
"fill": "#ffdf80"
}
]
}
```
Exports include the source revision in the filename, such as `first-sketch.r2.svg`. Your `.drawfather` source is retained. PNG and WebP support a transparent canvas; JPEG flattens transparency onto white. Display of inline previews depends on the MCP client.
Writes are atomic, updates check revisions, and per-drawing lock files prevent concurrent edits from silently overwriting one another. If a process crashes during an update, it may leave a `.lock` file: after confirming no DrawFather process is editing that drawing, remove that lock file and retry. If you manually edit a managed source file, increment its `revision` to invalidate stale agent updates and keep its v2 model consistent with its elements. An external edit starts a new history branch unless it matches an existing canonical snapshot. Copy `.drawfather-history/` and `.drawfather-backups/` with the source when transferring managed history. A source file alone remains portable and renderable.
## Use as a library
After building, local code can import the engine directly:
```js
import { readFile, writeFile } from 'node:fs/promises';
import { composeDrawing, exportDrawing, parseDrawing } from './dist/index.js';
const drawing = await composeDrawing({
template: 'process', title: 'Product launch', steps: ['Build', 'Test', 'Ship'],
});
await writeFile('my-sketch.drawfather', JSON.stringify(drawing, null, 2));
// Existing source: parseDrawing(JSON.parse(await readFile('my-sketch.drawfather', 'utf8')));
await writeFile('my-sketch.svg', await exportDrawing(drawing, 'svg'));
await writeFile('my-sketch.png', await exportDrawing(drawing, 'png'));
```
## Development
```sh
npm run build
npm test
npm run schema -- --check
npm run example
npm run gallery -- --check
npm pack --dry-run
```
`npm run example` first composes the three automatic examples from `examples/compositions/*.json`, generates the sketch comparison, semantic edit example, and visual repair example, and then renders every `.drawfather` file in `examples/` into SVG, PNG, JPG, and WebP in `output/`, refreshes the SVGs in `examples/`, and updates the README gallery from [examples/gallery.json](examples/gallery.json) and the actual source files. Edit a source file, then rerun this command to keep its image and code block in sync. `npm run gallery -- --check` verifies that the README embeds the current source. Tests cover validation, deterministic rendering, migration, semantic edits, groups, constraints, visual checks and repair limits, exact-plan application, export dimensions, history and write-failure recovery, revisions, storage boundaries, and real MCP client workflows. The test command runs a build first.
The example pipeline also builds the root gallery manifest/receipt and the illustrative project under `examples/project/`. Its known sample code and diagram are generated together; generic project rendering never acknowledges changed code. Run `npm run docs:check` after regeneration. The engine uses TypeScript, ELK, Rough.js, OpenType.js, Sharp, Zod, and the official MCP TypeScript SDK. Rendering requires no browser. Read [CONTRIBUTING.md](CONTRIBUTING.md) for the project scope and contribution workflow.
## License
The engine and repository files are licensed under [Elastic License 2.0](LICENSE), except `schema/` and `examples/`, which each include an MIT license. DrawFather is source-available / fair-code; the engine is not OSI open source.
You can self-host and modify the engine, including for internal business use, subject to ELv2. Offering substantial DrawFather functionality to third parties as a hosted or managed service requires separate permission from the relevant copyright holders. Access through MCP, an API, or an AI assistant can fall within this restriction. Commercial integrations depend on the functionality made available; they are not all prohibited. See [LICENSING.md](LICENSING.md) for examples.
Using DrawFather does not impose the engine license on your own drawing content or its `.drawfather`, SVG, PNG, JPG, WebP, or PDF exports. Third-party content and assets retain their own terms. Kalam is by Indian Type Foundry and uses the SIL Open Font License 1.1. Dependencies retain their own licenses; see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
TDQS
Scored across 23 tools
The four edit-style tools (edit_drawing, update_drawing, patch_drawing, edit_creative) overlap significantly; descriptions do attempt to differentiate them (patch for small label/style, update for full element upserts, edit for atomic operations), which helps but still requires careful reading. The three compose_* tools are separable by diagram domain, and check/repair/preview/export are distinct. Overall boundaries exist but are blurry in the mutation cluster.
Most tools follow a predictable snake_case verb_noun pattern (compose_*, create_drawing, edit_*, get_*, list_*, import_drawing, export_*, preview_drawing, check_drawing, repair_drawing, migrate_drawing). A few deviate: 'project_diagrams' (noun_verb) and 'drawing_history' (noun_noun), which break the pattern slightly. Still readable and largely consistent.
23 tools is on the heavy side for the stated scope; several (compose_drawing, compose_technical, compose_creative and the four editors) suggest consolidation is possible. It is defensible given the breadth of diagram types, interchange formats, and repair/history workflows, but it sits in the heavy band rather than a tight, well-scoped set.
Coverage is broad: create, read, edit, patch, import, export, preview, check, repair, migrate, history, and project management are all present, which is strong lifecycle coverage for a diagramming engine. However there is no explicit delete/rename tool for drawings, a minor gap an agent would have to work around via history or overwrite.