Under the hood
How a chat with Claude on a Mac turned into 1,044 illustrated scenes, Genesis, Exodus, Joshua, Judges, Ruth, 1 and 2 Samuel, 1 and 2 Kings, 1 and 2 Chronicles, Ezra, Nehemiah, Esther, the four Gospels and the Acts of the Apostles, plus a short film of Paul's letter to Philemon, rendered on a box on my desk. Everything here is in the GitHub repo, including the raw numbers.
The run, by the numbers
Nineteen books so far. Pick any two to compare side by side. It opens on the easiest book and the hardest, measured by how many attempts it took to get each published image.
Attempts per published image, every book
Show the full table for all nineteen books
| Genesis | Exodus | Joshua | Judges | Ruth | 1 Samuel | 2 Samuel | 1 Kings | 2 Kings | 1 Chronicles | 2 Chronicles | Ezra | Nehemiah | Esther | Matthew | Mark | Luke | John | Acts | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Test conditions | Box doing nothing else; run overnight with no one watching | Image agents one job at a time; two scenes redone at the end | Image agents one job at a time; one more pass on 10:16 at the end | Image agents one job at a time; two scenes redone at the end | Image agents one job at a time; box otherwise quiet | One session with 2 Samuel; three rounds of fixes after Brian's review | One session with 1 Samuel | Box doing nothing else | Box doing nothing else | Image agents one job at a time; other coding agents on the box for the first half hour | One session with 1 Chronicles | Image agents one job at a time; box otherwise quiet | Image agents one job at a time; box otherwise quiet | Image agents one job at a time; box otherwise quiet | Box doing nothing else, checked clean before the run | Three AI coding agents running on the box at the same time | Box doing nothing else, fresh reboot | Box doing nothing else | Box doing nothing else |
| Chapters | 50 | 40 | 24 | 21 | 4 | 31 | 24 | 22 | 25 | 29 | 36 | 10 | 13 | 10 | 28 | 16 | 24 | 21 | 28 |
| Scenes | 89 | 77 | 47 | 52 | 8 | 41 | 37 | 37 | 36 | 43 | 46 | 19 | 20 | 17 | 111 | 75 | 103 | 88 | 98 |
| New cast portraits | 11 (Adam to Joseph) | 7 (Moses at two ages, Aaron at two, Miriam, Pharaoh, young Joshua) | 5 (Joshua at two ages, Rahab, Caleb, Eleazar) | 5 (Deborah, Gideon, Jephthah, Samson, Delilah) | 3 (Ruth, Naomi, Boaz) | 9 (David at four ages, Samuel, Saul, Jonathan, the ark, Goliath) | 2 (Joab, Absalom) | 7 (Solomon at two ages, Jeroboam, Ahab, Jezebel, Elijah, Elisha) | 4 (Elisha old, Jehu, Hezekiah, Josiah) | 1 (Asaph) | 3 (Rehoboam, Asa, Jehoshaphat) | 3 (Ezra, Zerubbabel, Jeshua) | 1 (Nehemiah) | 4 (Esther, Mordecai, Haman, Ahasuerus) | 1 (Joseph) | 2 (Peter, Mary Magdalene) | 1 (Mary, mother of Jesus) | 2 (Jesus, John the Baptist) | 6 (Paul, John, Stephen, Barnabas, Silas, Philip) |
| Attempts (generations + edits) | 153 (129 + 24) | 266 (188 + 78) | 162 (106 + 56) | 205 (134 + 71) | 31 (21 + 10) | 140 (87 + 53) | 98 (56 + 42) | 96 (63 + 33) | 80 (49 + 31) | 151 (98 + 53) | 166 (106 + 60) | 68 (38 + 30) | 65 (37 + 28) | 71 (42 + 29) | 217 (163 + 54) | 189 (160 + 29) | 254 (194 + 60) | 168 (121 + 47) | 234 (162 + 72) |
| Attempts per published image | 1.53 | 3.45 | 3.45 | 3.94 | 3.88 | 2.80 | 2.51 | 2.18 | 2.22 | 3.51 | 3.61 | 3.58 | 3.25 | 4.18 | 1.94 | 2.45 | 2.44 | 1.87 | 2.25 |
| Kept on the first try | 57 of 89 | 8 of 77 | 7 of 47 | 6 of 52 | 0 of 8 | 8 of 41 | 7 of 37 | 6 of 37 | 8 of 36 | 2 of 43 | 1 of 46 | 2 of 19 | 3 of 20 | 0 of 17 | 38 of 111 | 10 of 75 | 20 of 103 | about half | 13 of 98 |
| GPU time | 53 min | 100 min | 68 min | 80 min | 12 min | 68 min | 51 min | 41 min | 34 min | 62 min | 67 min | 30 min | 28 min | 30 min | 91 min | 99 min | 108 min | 68 min | 115 min |
| GPU busy during the session | 69% | 65% | 54% | 52% | 60% | 54% (shared session) | 76% | 79% | 56% (shared session) | 62% | 59% | 62% | 58% | 57% | 57% | 44% | 63% | ||
| Median klein + reference job | 16.7 s | 17.2 s | 17.6 s | 16.5 s | 15.9 s | 16.8 s | 16.9 s | 16.8 s | 16.2 s | 16.7 s | 16.6 s | 16.0 s | 17.1 s | 16.0 s | 16.7 s | 21.7 s | 16.8 s | 15.7 s | 16.8 s |
| Median edit | 56.5 s | 43.2 s | 43.1 s | 42.8 s | 42.3 s | 55.7 s | 55.1 s | 40.5 s | 39.2 s | 42.5 s | 42.7 s | 41.9 s | 42.0 s | 42.2 s | 57.0 s | 91.9 s | 57.0 s | 52.5 s | 57.0 s |
Why the numbers differ
One cast for the Gospels
Matthew, Mark, Luke and Acts reused John's portraits, so the New Testament shares one cast. Each Old Testament book brought its own.
Mark: a busy box
Mark ran while a separate workflow had three AI coding agents working on the same machine. Its median edit took 92 seconds instead of 57.
Luke and Matthew: quiet again
On a quiet, freshly rebooted box, Luke's per-job times came back almost to John's, the clearest sign the agents slowed Mark. Matthew matched Luke almost to the tenth of a second.
Zooming in costs attempts
From Mark on, every image was checked region by region, which catches small things a full-frame look misses. That is most of why later books took more tries than John.
Acts: crowds and furniture
Same per-job times as Luke and Matthew, but more attempts: more crowds, Roman rooms full of modern furniture, and six new faces to keep straight.
Genesis: the easiest
Run overnight with no one watching, and the fewest attempts of any book. Much of it is sky, sea and animals, and every prompt started from the lessons of the books before it.
Samuel: aging and reviews
David had to age across four portraits, the ark needed its own reference and a rule that nobody touches it, and three rounds of Brian's review caught what the first pass let through.
Chronicles: the hardest so far
About three and a half attempts per image. The genealogies needed a picture for every chapter of names, the model could not count seven sons or two oxen, and the temple musicians came out with valved trumpets, lyres played with bows, and snare drums on stands.
Exodus: the sapphire and the door
The sapphire pavement of 24:10 came out as a wireframe grid, a plastic slab and a row of solar panels. The Passover door took fourteen tries: the model kept adding horror-film drips, a mushroom cloud, an electric light, a potted plant and a tornado.
Judges: chariots and jawbones
Four tries at Sisera's chariots drew boats, carriages and covered wagons, so the picture has horses in the rain instead. Samson's jawbone came out as a giant fanged arc, a tusk and a pickaxe before a bone the length of a forearm.
Joshua: battles
The first book allowed to show fighting, with some blood. Edits that removed a modern town from behind a battle often took the army or the flames with it, and in eight generations the model never put a captain's foot on a king's neck. An edit did it in the end.
Kings: chariots and castles
1 Kings fought glass jars, ceiling lights and a model that cannot draw a chariot. In 2 Kings every distant city came out as a medieval castle or a modern town.
Per-job data
Genesis CSV / summary, 1 and 2 Samuel CSV / summary, 1 Kings CSV / summary, 2 Kings CSV / summary, 1 and 2 Chronicles CSV / summary, Ezra CSV / summary, Nehemiah CSV / summary, Esther CSV / summary, Exodus CSV / summary, Joshua CSV / summary, Judges CSV / summary, Ruth CSV / summary, Matthew CSV / summary, Mark CSV / summary, Luke CSV / summary, Acts CSV / summary.
Housekeeping on the box
Before Luke, the Halo was rebooted to clear dozens of orphaned Chromium processes. Those didn't reappear during the Luke run, but a python3 process belonging to ComfyUI stayed resident after ComfyUI was closed properly, holding about 2.4 GB of memory. A full restart is needed to get the machine back to a clean state before the next run. Before Matthew the box was checked again and came back clean: only ComfyUI running, no language model loaded, 0.5 GiB of GPU memory in use.
John in detail
generations and edits for 90 published images (1.87 each)
of GPU time for the whole book, about 45 s per published image
each model was reloaded, from switching between generating and editing
of the active session the GPU was busy. The rest was prompting and review.
for the book, about 3 cents of electricity at $0.15/kWh
for the same 240 calls on cloud image APIs (estimate)
Every job came from ComfyUI's own history: comfy_history.csv. The scripts that produced these numbers are in scripts/, and every image with its prompt is on the Making of page.
Philemon: a short film
Philemon is one chapter, so it became a three-minute film instead of a picture book. The narration is the clock: every shot is timed to its verses, and the player moves the camera itself. Most shots are video clips made with LTX-2.5 from a still keyframe; the rest are stills with depth parallax, built from a depth map made on the Mac. A lyre score made with ACE-Step 1.5 runs under the voice and dips under the narration. Watch it here.
| Shots | 37: 26 video, 9 depth parallax, 1 pan, 1 still |
|---|---|
| Length | 3 min 12 s (narration 3 min 7 s, then a silent epilogue) |
| New cast portraits | 13: Paul about ten years older than in Acts, Onesimus, Timothy, a guard, Philemon, Apphia, Archippus, Tychicus, Epaphras, Mark, Aristarchus, Demas, Luke |
| Still attempts (generations + edits) | 225 (134 + 91) for the keyframes, portraits, character sheets and location plates |
| Video renders | 52 logged, 12 of them first-and-last-frame |
| GPU time | 164 min for stills, about 504 min for video, 16 min for music |
| Median klein + reference job | 52.8 s (keyframes at 3.7 MP, against about 1 MP in the books) |
| Median edit | 40.3 s |
| One video clip, draft quality (960 wide) | 5 s: about 3.5 min. 7 s: about 7.5 min. 11 s: about 20 min |
| First-and-last-frame clip | 5 s: about 10 min. 7 s: about 21 min. 10 s: about 48 min |
| Published film | 28 MB: video at 960x544, stills 2048 wide, depth maps, one music track |
Video time grows faster than clip length, so long clips were the expensive part. ComfyUI lost its job history in two restarts during the video work, so the video numbers come from the render logs. Per-job data: CSV / summary.
The pieces
Claude calls a tool like halo_generate_image. The MCP server fills in a ComfyUI workflow,
queues it, polls until the image is done, saves the PNG on the Mac and hands a preview back into the chat.
ComfyUI only listens on the Halo's localhost. The Mac reaches it through an SSH connection that launchd opens on demand,
so nothing is exposed on the network. Setup steps are in setup/.
The MCP tools
halo_generate_image
Text to image. Pick a model or take the default. Pass a reference_image to put the same face in a new scene, or a trained lora.
halo_edit_image
Plain-language edits with Qwen-Image-Edit. With no path it edits the last image, so fixes chain naturally.
halo_list_models
What's installed, each model's tuned defaults, and what it can do.
halo_train_lora
Uploads a folder of photos and trains a LoRA with ComfyUI's built-in trainer, with status and cancel tools alongside.
Code: mcp-server/ · workflows/ · scripts/ · viewer/ · setup/
The box
| APU | AMD Ryzen AI Max+ 395 ("Strix Halo"): 16 Zen 5 cores / 32 threads, up to 5.19 GHz, 64 MB L3 |
|---|---|
| GPU | Radeon 8060S, RDNA 3.5 (gfx1151), 40 CUs, up to 2.9 GHz |
| NPU | XDNA 2 (not used here) |
| Memory | 128 GB LPDDR5X shared: 512 MB VRAM carve-out, 94 GiB GPU-addressable via GTT |
| Power | AMD spec 55 W default, 45 to 120 W configurable. Measured: 30.6 W idle, about 156 W sustained under load |
| OS | AMD Ryzen AI Developer Platform 1, kernel 6.18 |
|---|---|
| ROCm | 7.14 |
| PyTorch | 2.12.0+rocm7.14.1 (HIP 7.14.60850) |
| ComfyUI | 0.21.1 in a rootless Podman container, socket-activated |
| Disk | 2 TB Micron 4600 NVMe |
The models
| Used for | Model | Precision | Size | Steps |
|---|---|---|---|---|
| Scenes and portraits | FLUX.2 [klein] 4B (distilled) + Qwen3 4B text encoder + FLUX.2 VAE | BF16 | 16.1 GB | 4 |
| Keeping faces consistent | Same, with a ReferenceLatent built from a cast portrait | BF16 | 4 | |
| Fixes and touch-ups | Qwen-Image-Edit 2511 + Qwen2.5-VL 7B + Lightning 4-step LoRA | FP8 (compute in BF16) | 31.0 GB | 4 |
| Video (Philemon) | LTX-2.5 22B distilled + Gemma 4 12B text encoder + video VAE + 2x latent upscaler | INT8 (VAEs in BF16) | 39 GB | 8 + 3 |
| Depth for parallax (Philemon) | Depth Anything V2 Large, on the Mac | FP32 | 1.3 GB | 1 |
| Music (Philemon) | ACE-Step 1.5 turbo (all in one) | BF16 | 10.0 GB | 8 |
The book's model set is 47.2 GB on disk. A 20B-parameter edit model on an integrated GPU works because the GPU borrows system memory: peak usage was 27 GB for klein and 38 GB for the edit stack.
Benchmark
Fixed seeds, 1 Hz power and temperature sampling on the box. Energy is APU socket energy, not wall plug. benchmark.csv · raw samples
| Job | Time | Avg power | Energy / image | Peak GPU memory |
|---|---|---|---|---|
| FLUX.2 klein, 1344×768, warm | 8.6 s | 119 W | 0.28 Wh | 27 GB |
| FLUX.2 klein, first run after unloading | 12.7 s | 98 W | 0.35 Wh | 27 GB |
| klein + reference image, warm | 15.7 s | 130 W | 0.57 Wh | 27 GB |
| Qwen-Image-Edit, warm | 31.9 s | 133 W | 1.18 Wh | 38 GB |
| Qwen-Image-Edit, right after a klein job | 54.3 s | 116 W | 1.75 Wh | 38 GB |
| Qwen-Image 2512, 1328×1328, warm | 22.3 s | 135 W | 0.84 Wh | 64 GB |
Under load the package holds a ceiling of about 156 W. The GPU edge sensor hit 100 °C on back-to-back Qwen runs, but the shader clock only dropped 0.8%, so it's power-limited rather than throttling.