My automated image layout kept putting speech bubbles over the character
Incident log · September 2026
- What ran
- An automated layout step in my content pipeline that places speech bubbles and captions on top of generated scene images and renders a six-panel image
- What I saw
- Speech bubbles landed on top of the character in the finished panels. Every render finished without an error, so nothing flagged it.
- Why
- Bubble positions came from fixed layout rules, while the character's position came from a generated image that changed every time. Nothing in the renderer knew where the character actually was.
- What it cost
- Caught in my own review of the first episode. Three of the six scenes were regenerated and the renderer was changed the same day.
- What changed
- Scene prompts now ask for the top third of the image to stay empty, and a guard measures how much of the character sits under each bubble. Over 2%, it moves the bubble to the other side or lower; if that does not work, the render fails instead of shipping.
What I expected
One part of my content pipeline turns a short script into a six-panel image, like a comic strip. Each panel is a generated scene: one or two characters in a room, a café, on a sofa. A renderer then lays out the page in HTML and CSS: panel borders, a speech bubble per line of dialogue, a caption strip, and a screenshot of the result as the final image.
The bubbles had layout rules. The side of the panel depended on who was speaking, and there were a handful of preset tail directions. I expected that, with the speaker on the left and the bubble on the left above them, the bubble would sit in the empty space over the character’s head.
What actually happened
On September 7, 2026, I reviewed the first full episode with generated scenes in every panel. Speech bubbles sat on top of the character instead of in the empty space above.
| Count or value | |
|---|---|
| Panels in the episode | 6 |
| Scenes regenerated with a new composition rule | 3 (panels 1, 3 and 4) |
| New scenes generated for the other panels | 2 (panels 2 and 5) |
| Overlap the guard allows | 2% of the area under a bubble |
| How the guard fixed panel 4 | Moved the image down 36 pixels, which brought it to 2% |
The renderer had reported success every time. A bubble over a character is not an error to a layout engine: the text fits, the box is drawn, the screenshot is taken. The only check was a person looking at it.
Why
There were two layers, and they did not know about each other.
The layout rules were fixed. They knew the panel size, the speaker’s side and the bubble size, and nothing else.
The scene was generated. The character could be anywhere in the frame: high or low, left or centre, large or small. A new image meant a new position.
So the bubble went where the rules said, and whether that was empty space was luck. Most of the time it was. When the generated character stood tall or leaned toward the bubble’s side, it was not.
An earlier problem the same day had made this possible to measure at all. The first character images had solid white backgrounds, so a character drawn on top of a small prop (a phone on a table) hid the prop completely. I added a step that turns the white background transparent. After that, “how much of the character is under this bubble” became a question with a numeric answer: count the non-transparent pixels.
What I changed
- Make room before generating. The scene prompts now ask for the top third of the image to stay empty. Panels 1, 3 and 4 were regenerated with that rule. Regenerating was cheap, a few cents per scene and about fifteen seconds each, so redoing a scene costs less than fighting a bad one in layout.
- Measure, then move. A small guard runs inside the renderer. For each bubble, it measures how much of the area under the bubble is covered by the character’s visible pixels. Above 2%, it tries the other side of the panel, then lower. In panel 4 it moved the image down 36 pixels, which brought the overlap down to the limit.
- Fail rather than ship. If no position works, the render fails. The answer is then a person’s decision: regenerate the scene or change which side the bubble goes on. A guard that quietly shrinks the bubble or the character would just move the problem somewhere less visible.
- Cover all the text. Later that day, a short video version of the same episode put a large title over the character in its opening frame. The guard had only been watching bubbles. It now also checks the title and caption text.
- Watch the side effects. That evening, one three-line bubble made the guard push the picture so far that the character came out noticeably smaller. The fix was in the script, not the guard: the line was cut to two lines.
The guard is now connected to both renderers in the pipeline, so a new layout type cannot skip it by accident.
What this has to do with hiring someone
If you hire someone to build anything that puts text on generated or user-supplied images, such as social cards, product images with prices, thumbnails or illustrated posts, you are buying the same two layers. The text rules are fixed and the image is not. A demo will be built from images where it happens to work, which is why reading a portfolio for what it does not show matters here.
Three questions are worth asking before you order:
- How does the build know where the subject is? Transparent backgrounds, a detected bounding box, a reserved empty zone in the prompt or the template. “We position it nicely” is not an answer.
- What happens when it cannot place the text cleanly? The better answer is “the render fails and tells you” rather than “it shrinks the text until it fits”.
- Which text does the check cover? Headings, captions, price tags and logos all count, not only the main line.
For acceptance, ask for a batch run on images you choose, including a few where the subject fills the frame, and look at every output, not a sample. The acceptance checklist for unattended automations has the wider set of tests for jobs that run without you, and the AI-built app acceptance lines are a good place to add this one when the layout is part of an app someone built for you.
What I put in a brief now
- When a layout combines fixed text with generated images, I ask how the build knows where the subject is, and what happens when the text would cover it.
- I ask for a rule that fails the render when it cannot place the text cleanly, rather than one that shrinks or hides things quietly.
- I ask for the overlap check to cover every kind of text the layout adds, not only the main one.