"Agents drawing diagrams" is the next big thing since "Agents writing prose".
Diagrams are more effective at communicating ideas than prose alone, and LLMs are great at
drawing and updating these diagrams quickly. But it's easy to get caught up in the
complexities of how to lay out components, and keep them looking neat. This is where "diagrams
as code" tools like D2 and Mermaid are effective for keeping conversations focused on
content without worrying about the layout.
LLMs read and write D2 and Mermaid script directly, without
needing to see diagrams laid out or rendered. This means a greater than 90% saving of token
spend versus using an SVG directly, and makes feedback much faster. With this comparison, plus
D2's newly open-sourced TALA renderer,
[1] I'll be switching to D2 for my system design diagrams.
Throughout this article we'll focus on a simple event processing service, rendering it as D2,
Mermaid and freestyle SVG diagrams. Notice the difference between constraints, layering, and
freestyle layouts.
D2 with the
TALA constraints engine
Mermaid with the Dagre layering engine
SVG based laid out manually by the LLM
All of the conversations, diagrams and token metrics were captured through my
Diagram Chat tool (which renders D2
and Mermaid client-side), and
deepseek-4.1-flash as the
LLM.
Updating a D2 diagram
Consider this chat conversation with an LLM. We can request a simple change to a diagram,
passing in the D2 script. The chat client renders the diagrams to SVG, but the LLM operates on
the script directly and never sees the rendered diagram.
User
Update this D2 diagram. A new decision node between the service and the queue. if it's the
3rd retry, send it to the deadletter queue.
Just one turn, and the tokens have already taken a bite out of the context window:
2,113 in, 15,017 out (12,739 thinking + 2,278 response).
Updating a Mermaid diagram
The D2 and SVG diagrams contrasted on token consumption, but Mermaid is a more fair
comparison. Let's make a similar change.
User
Update this Mermaid diagram. A new decision node between the service and the queue. if
it's the 3rd retry, send it to the deadletter queue.
xml
```mermaid
flowchart LR
ES@{ shape: cloud, label: "Event sources" }
EQ[["Event queue"]]
DLQ[["Dead letter queue"]]
subgraph EPS["Event processing service"]
direction LR
V{"Validate"}
D{"Deduplicate"}
FP["Further processing …"]
DISC["Discard"]
end
ES -->|"publish events"| EQ
ES -->|"undeliverable events"| DLQ
EQ -->|"deliver batch"| V
V -->|"valid"| D
V -->|"invalid"| DLQ
D -->|"new"| FP
D -->|"duplicate"| DISC
V -.->|"retry"| EQ
D -.->|"retry"| EQ
```
Assistant
Here's the diagram with the new decision node.
markdown
```mermaid
flowchart LR
ES@{ shape: cloud, label: "Event sources" }
EQ[["Event queue"]]
DLQ[["Dead letter queue"]]
RC{"3rd retry?"}
subgraph EPS["Event processing service"]
direction LR
V{"Validate"}
D{"Deduplicate"}
FP["Further processing …"]
DISC["Discard"]
end
ES -->|"publish events"| EQ
ES -->|"undeliverable events"| DLQ
EQ -->|"deliver batch"| V
V -->|"valid"| D
V -->|"invalid"| DLQ
D -->|"new"| FP
D -->|"duplicate"| DISC
V -.->|"retry"| RC
D -.->|"retry"| RC
RC -.->|"yes"| DLQ
RC -.->|"no"| EQ
```
Mermaid's token consumption is much better:
397 in, 696 out (475 thinking + 221 response). But the
layout is starting to get weird...
Direct comparisons
The tokens in this table are samples. In practice, I saw ±40% variance in these, depending on
how long the LLM took to think, and the complexity of its response. However the
scale still stands.
D2
SVG
Mermaid
Input tokens
361
2,113
397
Thinking tokens
460
12,739
475
Response tokens
201
2,278
221
Layout engines save a lot of tokens
D2 and Mermaid produce similar output token counts, but SVG is roughly 20 times more
expensive. This is partially due to the verbosity of the SVG format (very flexible, fully
manual layout), but mostly because the LLM is performing the layout manually as part of the
"thinking" stage. D2 and Mermaid just delegate that to the rendering engine.
...but not all layout engines are equal
The differences we see between D2 and Mermaid generally come down to how the layout engine
works:
D2 diagrams with the
TALA engine uses
a constraint-based model.
TALA's constraint-based layout optimises for short edges and
balanced whitespace. Dagre and ELK's layered graph style (AKA
Sugiyama graph style) assigns every node to a discrete rank. Each rank becomes a column, and
the diagram grows horizontally.
The SVG is effectively a free for all. You might end up with a legible diagram, you might not.
But every coordinate and path string is a token the model produced, and you can't verify its
correctness without looking at it.
What's next?
LLMs can generate D2 code as comfortably as Mermaid code, but most agent tooling can't render
it yet. Mermaid shows up extensively in markdown documents and LLM harnesses, while D2 has far
less support. I had to build
Diagram Chat with a custom
rendering plugin to test D2 with TALA alongside Mermaid and
SVG, and most agent harnesses would need similar work. Now that
TALA is open-sourced, [1] though, it could
land in Mermaid itself, which would also close that gap.
In the meantime, for iterating on diagrams over several turns, D2 with
Tala is the format that holds up. You may need to vibe-code
your own renderer, but it's worthwhile for the diagram quality and token consumption.