Coloring Pages with Local AI: From Prompt to a File That Prints Well
Generating the image is the easy part, and that's where most tutorials end. What determines whether the child can paint is what comes next: binarizing, vectorizing, and printing without jagged edges.
In a hurry? Ask Claude for the TL;DR — it reads the page and summarises it.
en Machine translation by qwen3:32b, reviewed by the author. Read the Portuguese original
My daughter asked for a dinosaur drawing to paint. I went looking online and found the usual: a 600-pixel PNG with a watermark in the middle, three ads around it, and a line that turns into a staircase as soon as it leaves the printer.
I have six machines here running a local model. I thought it would take ten minutes.
It took an afternoon, and the problem wasn’t where I expected. The image comes out easily. What’s laborious is getting it onto paper in a way a five-year-old can paint.
Which model, and why not the most famous one
black-forest-labs/FLUX.2-klein-4B
- Parameters
- 4 B
- License
- Apache 2.0
- Transformer
- 7.75 GB (BF16)
- Text encoder
- 8.04 GB (BF16)
- VAE
- 0.17 GB
- Total on disk
- 15.96 GB
Almost every tutorial uses FLUX.1 dev, which is the most well-known. I started with it too, until I stopped to read the license: it’s non-commercial.
For my daughter’s drawing, it doesn’t matter. But then her teacher saw it and asked for copies for the whole class, and the conversation changes. I’m not a lawyer and won’t pretend to know exactly where the line between personal use and distribution lies, but I found it simpler to use a model where this question doesn’t exist.
The 4B klein is Apache 2.0. What comes out of it is yours, no asterisk.
How much VRAM
| Precision | Transformer | Text | VAE | Total |
|---|---|---|---|---|
| BF16 (as published) | 7.75 | 8.04 | 0.17 | 15.96 |
| FP8 | 3.88 | 4.02 | 0.17 | 8.06 |
| Q4 (~31%) | 2.40 | 2.49 | 0.17 | 5.06 |
There’s a detail that changes the math on small cards. The text encoder only works while the prompt is being read. After that, it can leave memory, and the transformer finishes the job alone.
| Strategy | BF16 peak | FP8 peak |
|---|---|---|
| All loaded together | 15.96 | 8.06 |
| Encoder unloaded after prompt | 8.04 | 4.04 |
In FP8 with unloading, the theoretical peak drops to 4 GB. That changes who gets to play with this: it fits even on an 8 GB card.
Does it fit on my cards?
| Machine | Memory (GB) | Full BF16 | FP8 | FP8 + unloading |
|---|---|---|---|---|
| RTX 5070 · 12 GiB | 12.9 | no | fits | room to spare |
| RTX 5070 Ti · 16 GiB | 17.2 | tight | room to spare | room to spare |
| RTX 4090 · 24 GiB | 25.8 | fits | room to spare | room to spare |
| RTX 5090 · 32 GiB | 34.4 | room to spare | room to spare | room to spare |
| MacBook Pro M4 Max · 48 GiB | 51.5 | fits | room to spare | room to spare |
| Mini PC · 96 GiB unified | 103.1 | room to spare | room to spare | room to spare |
The 12 GB 5070 is the interesting case. In LLM posts it often gets left out: it can’t load a 27B, can’t load a 32B, always watching the others. Here it runs everything with room to spare in FP8. Image generation is far friendlier to a modest card than text generation is, and nobody says so.
On Mac, with MLX
The Mac path isn’t the same. CUDA doesn’t exist there, and using PyTorch with MPS backend works but it is the worst of both worlds.
What’s used is mflux, a reimplementation of Flux in MLX, Apple’s framework. It already supports the klein family, has built-in 4 and 8 bit quantization, and a --low-ram flag for tight machines.
uv tool install mflux
mflux-generate \
--model klein \
--quantize 8 \
--steps 4 \
--height 1440 --width 1024 \
--prompt "coloring book page, thick black outlines, white background, a friendly dinosaur wearing rain boots" \
--output dino.png
Works well. It’s just slower, and it’s worth understanding why.
Why Mac takes longer
This bothered me for a while, because on LLM tests Macs often perform well. An M4 Max can keep up with a good GPU generating text. Then you ask it to generate an image and the difference is glaring.
It’s not a contradiction. The two problems stall in different places.
Text generation, in the decoding phase, is basically reading the entire weights out of memory for every token generated. The calculation itself is small; the bottleneck is the transport. What matters is bandwidth, and that’s exactly where unified memory competes equally.
Image generation is another problem. The model passes twenty, thirty times through the same latent, and each pass is a large matrix multiplication. The weights are already loaded and stay put; what runs nonstop is calculation. What matters here is FLOPS.
| Machine | TFLOPS | Where this matters |
|---|---|---|
| RTX 4090 | 82.6 | image generation |
| Apple M4 Max | 18.4 | image generation |
A 4.5 times difference in raw calculation. Add to that the fact that diffusion kernels have been optimized for CUDA for years and for MLX for much less time, and the result is what you see.
Order of magnitude to calibrate expectations: public reports put the 4B klein at 30-40 seconds per 1024px image on an M1 Max. I haven’t measured on my M4 Max yet, and won’t pretend I have.
The practical conclusion is annoying but useful: if you have an NVIDIA GPU and a Mac, generate on the PC and edit on the Mac. And if you only have a Mac, the 4B klein is the right choice precisely because it’s small.
The prompt
A coloring page isn’t a pretty drawing. It’s a drawing with lots of constraints:
coloring book page for young children, black and white line art,
thick clean bold outlines, uniform line weight, pure white background,
no shading, no hatching, no gray tones, no texture,
large simple closed shapes, centered composition, generous margins
Each part there is fixing something that went wrong before.
Thin lines disappear in a home printer and the child can’t see where to stop, hence thick bold outlines. Shading becomes a sprinkle of dots on paper and nobody can paint over it, hence no shading, no gray tones. Open regions make the crayon bleed, and regions too small are pure frustration for a five-year-old hand, hence large simple closed shapes. And generous margins because home printers don’t print to the edge, which I learned the hard way.
After that comes the theme, and here it pays to be specific: a friendly dinosaur wearing rain boots, standing next to a puddle.
Let the child choose. It’s the best part, and also where the process stalls, because children’s requests always have a detail the model ignores or misinterprets. I generate four at once and let her point. It goes faster than trying to get it right first time, and choosing is already half the fun.
The part tutorials don’t tell you
This is where the videos end: generated the PNG, sent to print, done.
But the PNG is the wrong format for this. A 1024×1024 image printed on an A4 sheet gives about 90 DPI. Home printers work between 300 and 600. The result is visible stair-stepping, exactly the problem with stock images that made me start all this.
Generating at higher resolution helps little and costs more. There is another way: black line on a white background should not be pixels, it should be vectors.
It’s two operations. First binarize, pushing everything to pure black or white, which incidentally cleans up the grays the model insists on leaving. Then vectorize, converting the contours into curves.
# 1. Binarize: gray becomes pure black or white, no middle ground
magick desenho.png -colorspace Gray -threshold 62% -type bilevel desenho.pbm
# 2. Vectorize: contours become curves
# --turdsize discards loose specks (noise from the generation)
# --alphamax controls how much the corners get smoothed
potrace desenho.pbm --svg --turdsize 12 --alphamax 1.0 -o desenho.svg
# 3. A4 PDF, ready to print at any size
magick -density 300 desenho.svg -page A4 -gravity center -extent 2480x3508 desenho.pdf
The --threshold 62% is the number you’ll keep adjusting. Too low makes the line thick and closes details. Too high breaks the trace and opens regions, which is worse, because crayon bleeds through even a one-pixel hole.
After the SVG the print size stops being a problem. The same file prints sharp on A4 or a 1.5-meter panel for the bedroom wall. And it stays in the tens of KB instead of megabytes, which matters when sending to the school parents’ group.
The test that really matters
No number here answers if the page works. The test is giving crayon to a child and watching.
Does she see where the line closes? Do the regions fit her hand? Does the drawing hold up to being painted over without turning into a blur?
I threw out a few that looked beautiful on screen but didn’t work on paper. Elegant, too thin lines. Details requiring adult precision. The model can’t know this, and no prompt fixes it.
What I haven’t measured yet
Seconds per image on each machine in the bench, including the M4 Max. How many generations are usually needed before a usable page comes out, which in my afternoon was about one in five. And if the 4B klein loses to its 9B sibling precisely in line art, where I suspect smaller models do better, because the constraint here is simplicity and not detail richness.
This last comparison is what I want to make. It’ll be for the next one.
Sources: FLUX.2-klein-4B on Hugging Face · mflux · potrace