# I Replaced Image AI for Technical Diagrams with an 8-Tool Code-First Matrix

> Source: <https://dev.to/nomurasan/i-replaced-image-ai-for-technical-diagrams-with-an-8-tool-code-first-matrix-3f9n>
> Published: 2026-06-30 06:45:34+00:00

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.

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:

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 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.

I am including small runnable snippets I can reuse.

``` php
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.

``` php
@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.

```
# 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.

``` php
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.

``` python
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.

``` python
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.

``` python
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.

``` python
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.

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.

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.*
