I gave one model the same 44-node architecture twice.
The first time I asked for raw SVG — place every box, route every edge, hand me the coordinates. The second time I asked it to describe the same system as typed JSON and let a layout engine draw it. Same model, same session, same brief. The only thing I changed was the output boundary.
The boxes are fine in both. I want to be upfront about that, because the usual version of this pitch is out of date. Models place labeled boxes well now. If you ask a current frontier model for a six-box flowchart as SVG you get a clean six-box flowchart, and if that's what you need, go do that — it's the right tool and I'm not going to pretend otherwise.
What broke was the edges.
With no routing algorithm the model just drew long diagonals straight through unrelated boxes. Not a few — everywhere the graph got dense. And when I changed one node, the entire hand-placed coordinate layout had to be regenerated, and came back different.
That second part is the one that actually annoyed me. It's not a rendering bug you can squint past. The picture is a dead artifact: you can't diff it, you can't edit one box, you can't get the same one twice. Every change is a full regeneration and a fresh roll of the dice.
This isn't a "wait for a better model" problem
Here's the part I'd push back on if someone else wrote it, so let me make the case.
Routing a connector around obstacles across a nested graph is global constraint optimization. It's the specific thing layout engines like ELK exist to solve. A model emitting SVG has to commit to an x/y for every point, in order, with no way to backtrack once it sees the whole picture — it's predicting the next token, not solving a layout.
So a better model gives you nicer boxes, not untangled edges. The failure is structural, and I'd expect it to reproduce across models past a couple dozen nodes. If you don't buy that, the honest move is to test it: throw a 40-node architecture at whatever model you trust and look at the edges. (I wrote up the method behind that image — same model, one shot, no correction pass — in a separate post, linked at the bottom.)
The conclusion I landed on: a drawing is the wrong output type. Not "AI can't make diagrams." The boundary is in the wrong place.
So move the boundary
Let the model do the thing it's genuinely good at — describing what the diagram means — and let a real engine own the spatial math. That's Glyphic. The model emits plain typed JSON. No coordinates, no DSL grammar to typo:
{
"type": "flowchart",
"nodes": [
{ "id": "spec", "label": "Typed JSON" },
{ "id": "engine", "label": "Layout engine" },
{ "id": "out", "label": "SVG / PNG" }
],
"edges": [
{ "source": "spec", "target": "engine" },
{ "source": "engine", "target": "out" }
]
}
ELK computes positions and routing (d3 for the data types), and the SVG gets rasterized to PNG natively in Rust via resvg. Three things about that hold up no matter how good models get.
Validation is a contract, not a crash. The JSON hits a strict Zod schema before anything renders. When the model gets it wrong you get back edges[2].target references unknown node 'paymentss' — precise, and fixable on the next turn. That's what makes generate → validate → fix → render loops actually work. A DSL like Mermaid parse-crashes on one typo and hands the agent nothing to act on.
There's no browser in the stack. Every "render diagrams server-side" path I tried ended up shelling out to headless Chromium — ~300MB and a cold-start tax on every invocation. Layout and rasterization here are both native, so it deploys to a Lambda, a CI job, or an agent loop as an ordinary Node dependency. That's an infrastructure fact, not a claim about model quality, which makes it the most durable thing I can say about the project.
It stays cheap at scale. Hand-drawing a big diagram is thousands of coordinate tokens — slow, and liable to blow the output ceiling and truncate into a broken render. Compact JSON in, geometry generated deterministically.
And because the JSON is the source of truth, the diagram stays editable data. Diff it, change one node, re-theme, re-render. Not a picture you regenerate from scratch and hope.
What you actually get
One call gives you SVG, a high-res PNG, and React Flow JSON. There are 18 diagram types behind the one schema — architecture with nested VPCs and clusters, sequence, ERD with crow's-foot notation, UML class, state machines, flowcharts, Gantt, timelines, Sankey, Git trees, mindmaps, C4, pie, quadrant, kanban, user journeys, treemaps, and a freeform canvas. Every one of them is rendered in the gallery from its exact JSON input, so you can judge the output yourself instead of taking my word for it. Theming, any Google Font, FontAwesome icons, and a hand-drawn sketch style are in there too.
Three ways to run it, same engine underneath.
As an MCP server, which is the 30-second version — no install:
claude mcp add glyphic -- npx -y @glyphicjs/mcp-server
Then just ask: "Use Glyphic and draw an ERD for a blog with users, posts, and comments." The model emits the JSON, calls the tool, diagram appears. Works in Cursor, Claude Desktop, VS Code, Windsurf — anything that speaks MCP.
As a library:
npm install @glyphicjs/core @glyphicjs/schema
import { processDiagram } from "@glyphicjs/core";
import { writeFileSync } from "node:fs";
const result = await processDiagram({
type: "architecture",
title: "Web App",
nodes: [
{ id: "web", label: "Web App", shape: "rounded", icon: "fab-react" },
{ id: "api", label: "API", shape: "hexagon", icon: "fas-bolt" },
{ id: "db", label: "PostgreSQL", shape: "database", icon: "fas-database" },
],
edges: [
{ source: "web", target: "api", label: "REST" },
{ source: "api", target: "db", label: "SQL" },
],
});
writeFileSync("diagram.png", result.png); // high-res PNG
writeFileSync("diagram.svg", result.svg); // scalable SVG
console.log(result.reactFlow); // interactive React Flow JSON
Or self-hosted behind your own HTTP endpoint, if you'd rather not ship the library to every client.
The license, plainly
Schema and MCP server are MIT. The core engine is FSL-1.1 — use it, modify it, self-host it; the only restriction is you can't resell it as a competing hosted service, and it converts to Apache-2.0 after two years. That's source-available with a delayed open license, not OSI-approved from day one, and I'd rather say that than overclaim it.
Try it
- Playground, no sign-in: https://glyphic.web.app/generate
- Repo: https://github.com/MS-Teja/Glyphic
- Wondering whether that before/after is a fair test rather than a strawman? I wrote up the method: Is the AI-diagram comparison fair?
I built this solo. If you're building agents, pipelines, or a product that needs diagrams, I'd love your feedback and a star helps.
Happy to go deep in the comments on the ELK layout choices, the resvg tradeoffs, or the schema design.

Top comments (1)
This is a strong example of choosing the right output boundary.
The same principle shows up all over agent tooling: let the model express intent and relationships, then let deterministic software own the parts that require global consistency.
For diagrams, that means JSON for structure and a layout engine for geometry.
For databases, it means natural language for intent and typed/query-safe layers for execution.
For workflows, it means the model proposes the next action and orchestration code owns retries, state, and side effects.
The mistake is asking the model to emit the final artifact when the final artifact contains hidden constraints the model cannot reliably maintain token by token. Coordinates, SQL side effects, workflow state transitions, permission boundaries -- all same family of problem.
Typed intermediate representations are underrated because they look less impressive in a demo. But they are the difference between “generated once” and “maintainable system.”