I needed faster edits for technical diagrams, and a lower recurring overhead for recurring visuals.
I stopped asking for new images for everything.
That change started the moment I replaced "generate now, tweak later" with a fixed 8-tool matrix.
TL;DR:
I moved recurring illustration work into seven scriptable stacks + one 3D stack and kept image-generation AI only as a fallback.
Why I rewrote this workflow
When I edited an article recently, I was spending too much time redoing the same visual shape in slightly different versions.
The same chart logic should not need prompt guessing each time.
I asked myself:
- Can this be represented as text or code?
- Can I regenerate it exactly when requirements change?
- Do I need raw design freedom, or do I need deterministic structure?
If the answer was mostly "text/code + deterministic output," I did not open an image-generation model first.
I also kept one practical boundary: this was not an academic tool roundup.
This is a log of what I actually used and in what context.
The number that changed my mind: an 8-tool decision matrix
The number I now defend is exactly 8.
Instead of inventing synthetic savings, I evaluate every new illustration request against this matrix.
| Tool | Best fit | Why I pick it |
|---|---|---|
| Mermaid | flow, sequence, architecture notes | fastest in markdown-native writing |
| PlantUML | UML-heavy docs | strict structure when Mermaid gets too loose |
| Markmap | map-style summaries | converts headings directly |
| Graphviz | dependency and direction graphs | compact graph semantics |
| matplotlib | numeric visualizations | source-of-truth from data tables |
| Pillow | labels, badges, annotations | deterministic pixel edits in Python |
| D3.js | node/link or hierarchy interactions | data-driven relationship rendering |
| Blender | 3D explanatory graphics | stronger structural clarity for complex scenes |
This is the exact set I now reach for before any image-generation request.
What happened first: practical snippets
I am including small runnable snippets I can reuse.
1. Mermaid for deterministic flow maps
flowchart LR
A["User"] --> B["App"]
B --> C["API"]
C --> D["Storage"]
C --> E["Cache"]
npm i -D @mermaid-js/mermaid-cli
I use this for quick reviews because it is fast to read, fast to version-control, and fast to regenerate.
2. PlantUML for strict structure
@startuml
actor User
participant API
participant DB
User -> API: Request
API -> DB: Query
DB --> API: Result
API --> User: Response
@enduml
java -jar plantuml.jar -tpng architecture.puml
When a diagram should model lifecycle, protocol, or strict roles, this is my second branch after Mermaid.
3. Markmap from markdown headings
# Release Plan
## Week 1
### Audit
### Diagram targets
## Week 2
### Implementation
### Regression checks
## Week 3
### Publish preparation
npm i -D markmap-cli
This removes a whole "I have to learn a separate visual DSL" step for internal notes.
4. Graphviz for dependency graphs
digraph G {
rankdir=LR;
"API" -> "Auth";
"API" -> "Search";
"Auth" -> "DB";
"Search" -> "SearchIndex";
}
dot -Tsvg graph.dot -o graph.svg
I use this when relationship direction is the only thing I need to make obvious.
5. matplotlib for reproducible data visuals
import matplotlib.pyplot as plt
stages = ["Flow", "Auth", "Search", "Storage", "Cache"]
latency = [1.2, 0.7, 2.1, 0.9, 0.4]
plt.figure(figsize=(7, 3.5))
plt.plot(stages, latency, marker="o")
plt.title("Pipeline Latency by Stage")
plt.ylabel("Seconds")
plt.tight_layout()
plt.savefig("pipeline-latency.svg")
uv add matplotlib
For this kind of visual, AI image generation is the wrong tool.
Data should be generated from data.
6. Pillow for labels and annotations
from PIL import Image, ImageDraw, ImageFont
canvas = Image.new("RGB", (640, 200), "#1f2d3d")
draw = ImageDraw.Draw(canvas)
draw.rectangle((20, 40, 620, 160), outline="#f4d03f", width=3)
draw.text((40, 80), "Deployment Checklist", fill="#ffffff")
canvas.save("badge-note.png")
uv add Pillow
I use this for simple, repeatable badges and annotations where consistency matters more than illustration style.
7. D3.js for flexible network diagrams
import { JSDOM } from "jsdom";
import * as d3 from "d3";
import fs from "node:fs";
const width = 540;
const height = 360;
const nodes = [{id: "A"}, {id: "B"}, {id: "C"}];
const links = [{source: "A", target: "B"}, {source: "B", target: "C"}];
const dom = new JSDOM("<!doctype html><body></body>");
const body = d3.select(dom.window.document.body);
const svg = body.append("svg").attr("viewBox", `0 0 ${width} ${height}`);
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(110))
.force("charge", d3.forceManyBody().strength(-220))
.force("center", d3.forceCenter(width / 2, height / 2));
simulation.tick(80);
svg.selectAll("line")
.data(links)
.join("line")
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
svg.selectAll("circle")
.data(nodes)
.join("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 18);
fs.writeFileSync("network.svg", body.html());
npm i d3 jsdom
When relationship density grows, D3 gives me the control that static diagram tools sometimes hide.
8. Blender for 3D explanatory scenes
import bpy
bpy.ops.wm.read_factory_settings(use_empty=True)
camera = bpy.data.objects["Camera"]
camera.location = (4, -6, 3)
camera.data.lens = 40
cube = bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 1))
sphere = bpy.ops.mesh.primitive_uv_sphere_add(radius=0.6, location=(2, 0, 0.6))
bpy.ops.render.render(write_still=True, filepath="infra-overview.png")
blender --background --python render_scene.py
Blender is the last tool I keep for cases where shape and spatial composition are part of the explanation.
The rule I extracted
I call it the "draw from intent, not from prompt" rule:
If the figure has structure, use text/code and regenerate it from source. Use image-generation AI only for final polish or style-first deliverables.
This removed most of the recurring "I know the idea but cannot get the same output again" pain.
Try it yourself
- Choose one article section that currently uses an image-style AI file.
- Convert it to one of the 8 matrix buckets.
- Replace one graphic with the smallest corresponding snippet above.
- Regenerate after one parameter/text change and compare the maintenance speed.
- Mermaid for control-flow logic
- PlantUML for strict protocol or class views
- Markmap for knowledge maps
- Graphviz for dependency direction
- matplotlib for numbers
- Pillow for labels and badges
- D3.js for link-heavy visuals
- Blender for 3D structure-only cases
If I had to do one thing now, I would first swap one recurring illustration with Mermaid or matplotlib and leave the rest unchanged until the matrix habit becomes automatic.
I can ship fast when visuals are generated like code.
Top comments (0)