Overview
I used to make diagrams with Mermaid: drop a code block into Markdown and a figure appears. That works fine, and AI is good at Mermaid.
But Mermaid only goes so far. For richer figures you need a drawing tool, and hand-drawing is expensive.
This post explores how to produce blog diagrams with AI.
Goals
- Describe a figure in text; AI draws it from that description.
- Stable output — not a surprise every run. The same brief should yield the same figure; changing one word should only change what it ought to.
- Local generation — no token burn.
Exploration
I found five approaches.
Rough.js
Idea. Rough.js is a hand-drawn style canvas library. With a handwritten font, rectangles and lines pick up jitter and look like a sketch.
Brief.
// x / w for each box is hand-computed
box(doc, 30, 60, 150, 130, 'gray', 'Power on', ['kernel + init']);
box(doc, 227, 60, 195, 130, 'gray', 'Boot script', ['/etc/init.d/codesyscontrol']);
box(doc, 469, 60, 225, 130, 'blue', 'Runtime', ['codesyscontrol.bin']);
// two more boxes omitted
arrow(doc, 181, 125, 221, 125);
Result:
Pros. Full control of layout; not limited by a layout engine.
The figures in this post use this method.
Rough.js + auto layout
Idea. Same drawing path, but coordinates are not hard-coded: measure text width, size the box, center the row.
Brief.
// content only — no positions or widths
flowRow(doc, [
{ tone: 'gray', title: 'Power on', sub: ['kernel + init'] },
{ tone: 'gray', title: 'Boot script', sub: ['/etc/init.d/codesyscontrol'] },
{ tone: 'blue', title: 'Runtime', sub: ['codesyscontrol.bin'] },
{ tone: 'orange', title: 'Read boot app dir', sub: ['.app + .crc'] },
{ tone: 'green', title: 'Verify & load', sub: ['enter cyclic tasks'] },
]);
Result:
Pros. Edit copy without re-placing boxes. Overflow fails loudly instead of relying on eyeballing the PNG.
D2 --sketch
Idea. D2 is a diagram DSL. Nodes and edges go in text; the layout engine places them; --sketch adds hand-drawn jitter. Export SVG → PNG. Fonts and Chinese line-height are the usual pitfalls.
Brief.
direction: right
power: "Power on\nkernel + init" { shape: rectangle }
script: "Boot script\n/etc/init.d/\ncodesyscontrol" { shape: rectangle }
rt: "Runtime\ncodesyscontrol.bin" { shape: rectangle }
// two more nodes omitted
power -> script -> rt -> read -> load
Result:
Pros. Best auto-layout of the five — a few lines of DSL and the structure is placed for you.
HTML/CSS + screenshot
Idea. Boxes as HTML, layout via CSS flex, then headless browser (Playwright) to PNG. Cost: you install a browser.
Brief.
<div class="flow">
<div class="node" style="border-color:#495057;background:#f1f3f5">
<div class="t">Power on</div>
<div class="s">kernel + init</div>
</div>
<svg class="arrow">…</svg>
<div class="node">…</div>
</div>
.flow { display: flex; align-items: center; gap: 6px; }
Result:
Pros. Web layout powers: wrapping, spacing, radii. Tweak styles in the browser.
Mermaid handDrawn
Idea. Mermaid’s built-in hand-drawn theme (look: handDrawn). Long Chinese labels often break the layout and need tuning.
Brief.
flowchart LR
power["Power on<br/>kernel + init"]:::tgray
script["Boot script<br/>/etc/init.d/<br/>codesyscontrol"]:::tgray
rt["Runtime<br/>codesyscontrol.bin"]:::tblue
power --> script --> rt
classDef tgray fill:#f1f3f5,stroke:#495057
classDef tblue fill:#e7f5ff,stroke:#1971c2
Result:
Pros. Lowest friction — no install; a code block in Markdown is enough. AI knows it well.
The five differ mainly in who computes coordinates and how much style you can control. All are code-generated figures.
How I actually use it
In daily writing I stay in lazy mode.
- Write
concept.mdfirst: outline, section leads, and the idea chain in plain words. - Let AI fill prose. Where a figure is needed, insert a figure brief: what to draw, layout, what to emphasize.
- Human review and edit.
- AI generates again and replaces the brief with the image.
Example brief:
Figure
- Content: full write→figure pipeline, 5 steps
- Layout: five boxes in a row, arrows between them
- Emphasis: the only human step — human review; width 1200px
While writing this section I literally typed the brief like this:
Then AI produced the figure above.
Sample output quality
A couple of local samples:
Mermaid would struggle with figures like these. Dragging boxes in an online tool burns your fingers. Calling a large image model burns tokens and often “surprises” you on edits.
With a local pipeline the artifact is a file on disk: fix it by hand (re-render) or say what to change — the next run stays stable.
Integration
Treat figure generation as a skill, or fold it into Agents.md — whatever matches how you want the loop to run.
Attachment
Full code for all five methods:
I only just started on this path — better ideas welcome.









Top comments (0)