1The failure
An image model given a product photo and a brief does two different jobs at once. It builds a scene, and it reproduces one specific object inside that scene. It is good at the first and unreliable at the second.
The output is not obviously broken, which is the problem. The logo is nearly the logo. The hardware is the right shape and the wrong finish. The colour has moved half a shade. Nothing in the frame looks wrong until it sits next to the real product on a listing page.
For a moodboard that is acceptable. For a catalogue it is not, and there is no partial credit: the object in the picture is the object a customer is being asked to buy.
A model asked to invent and to reproduce in one pass will invent. That is not a prompt problem.
We spent a while treating it as one. Longer briefs, stricter wording, explicit “do not alter the logo” clauses. All of it helped a little and none of it held across a set, because a single pass has no separate step where the product is the only thing being decided.
2Splitting the render
So the pass got split. Stage one renders the frame with nothing pinned in it. Stage two puts the real product into that frame.
Stage one is a base model built for real people and real light. It decides scene, pose and composition. It is never asked to imagine the product, and it could not edit one in afterwards if we wanted it to: it generates and cannot edit. That limitation is what keeps the two stages honest about which one owns what.
Stage two has the frame already. It is not deciding lighting or pose, so the one thing it is deciding is the one thing the first engine was worst at.
| Stage | Decides | Is shown |
|---|---|---|
| 1. Base render | Scene, pose, composition, light | The written prompt only |
| 2. Composite | The pinned product or face, and nothing else | The base render plus the real reference photographs |
When nothing is pinned, there is nothing for stage two to do. It degrades to a light refine or is skipped, and the shot is not billed for a pass that had no work in it. Running it anyway for symmetry would have been the tidier code and the wrong bill.
3The bug that changed the design
The first version of stage two built its edit instruction from the product record: name, category, the fields we already had. That is the obvious implementation. It is also wrong, and it fails in the most expensive way available, which is confidently.
A white tank top came back as a shoulder bag. Well lit, well placed, correctly composited, and the wrong category of object entirely.
The editor was not broken. The editor did exactly what it was told. We had told it the wrong thing.
The instruction writer had never seen the garment. It had seen a string, inferred an item type from it, and written a fluent instruction about an object that did not exist. Every layer downstream then executed that instruction correctly.
The fix was to stop describing and start showing. Stage two is now a vision pass: it is handed the base render and the actual reference photographs, and writes the instruction from what it can see. It names the real object because it is looking at the real object.
This is a general lesson we keep relearning. Anywhere a model is asked to reason about a specific real thing, give it the thing. A name is a lossy encoding and the model will fill the loss in silently.
4What the edit instruction may say
The instruction is narrow on purpose, and most of the rules are there because the broad version misbehaved.
It never re-describes the scene. Background, pose and light already exist in the base render. An instruction that mentions them invites the editor to redraw them, and a redrawn background means stage one's work was wasted.
It states what to preserve, not only what to change. An edit that only names the change is read as permission to touch everything else.
It is written in editing verbs. Replace, keep, match. Generation verbs pull the model back toward making a new picture.
When the vision pass fails
It falls back to a deterministic instruction and the render continues. An ordinary shot beats a failed shot, and a pipeline that stops because one reasoning step timed out is a pipeline that loses work it already paid for.
5One photograph, not twenty
The instinct when pinning something is to hand over every angle you have. It makes the output worse. Multiple references pull the subject in different directions and it drifts from shot to shot across a set, which is the exact thing pinning was supposed to prevent.
So: one angle per product, one photograph per character, face on and well lit with nothing else competing in the frame. The restriction is the feature.
Almost none of the work here was discovering that. It was enforcing it in the places a well-meaning user or a well-meaning agent would route around it.
6What this does not fix
Two passes cost more than one. A composited shot bills both stages. The skip path in figure 1 is not an optimisation, it is the only reason the cheap case stays cheap.
Stage two cannot rescue stage one. If the base render puts the subject in a pose where the product would be half out of frame, the composite has nowhere to put it. It can only place the object into the picture that exists.
A described place is not a real place. Generated locations hold up. A specific real building, a client's actual store, cannot be produced from words and still needs a photograph.
The vision pass is another model call. It is one more thing that can be slow, and its failure mode is a worse instruction rather than a loud error, which makes it harder to notice than a crash would be.
7Components
The base models come from Higgsfield.[1] They are why the base frame holds a real face and real light.
The compositing pass and the rest of the image and video models run through fal.[2] Which editor a shot gets is configuration rather than a constant, which is what lets a flagged render route to a model that can take it instead of failing at the gate.
What is ours is the split, what stage two is shown, and the reference discipline. The other half of the system, deciding what a given brand's shot should look like in the first place, is a separate note.
Questions
- Why do AI product photos get the logo or stitching wrong?
- Because a single pass is doing two different jobs. Building a scene is generative work and reproducing one specific object is not, and a model asked for both in one request will invent the parts it cannot recall. The output is not obviously broken, which is why it ships: the logo is nearly the logo and the colour has moved half a shade.
- How does a two-pass render fix it?
- The first pass renders the frame with nothing pinned in it, so it is never asked to imagine the product. The second pass already has the frame and only has to place the real product into it, which means the one thing it decides is the one thing a single pass was worst at.
- Why show the second pass the photograph instead of the product name?
- Because a name is a lossy encoding and the model fills the loss in silently. Building the edit instruction from the product record returned a fluent, well-composited render of a shoulder bag when the product was a white tank top. Handing the pass the actual reference images removes the guess: it names the object because it is looking at the object.
- Should I upload every angle of my product?
- No. One angle per product and one face-on photograph per person. Multiple references pull the subject in different directions and it drifts from shot to shot across a set, which is the exact thing pinning is supposed to prevent.
- Does a shot with nothing pinned still cost two passes?
- No. With nothing pinned there is nothing to composite, so the second pass degrades to a light refine or does not run, and the shot is not billed for work that had none in it.
References
- [1]Higgsfield the models that render the base frame.
- [2]fal runs the compositing pass and the rest of the image and video models.
- [3]Vercel AI SDK, issue 8180: tool-result image parts are dropped why references are attached as message content, not returned from a tool.
- [4]Inngest durable execution; a render survives the browser that started it.
- [5]Schema.org TechArticle the structured description this page publishes about itself.
