Hey all, happy Friday,

Last weekend, we drove out to my dad and stepmom's place, where James debuted his newest skill: the word no, delivered with a firm shake of his finger.

We'll be playing catch: he hands me the ball; I ask for it back, and he shakes his head, wags his index finger, makes firm eye contact, and says, "No, no, no, no."

Based on this (among other things), my dad pronounced, Prince James just got upgraded to King James.

(The original commissioned the most famous Bible in the English language. Ours has seemingly commissioned the word no.)

Here is the part that had Maddie and me laughing. He used to crawl over to Linus’ food and water bowls and help himself, and we spent months telling him no. It finally landed, and then it overcorrected. Now he walks up to the bowls, stops, shakes his head, and informs us, “No, no. no.” He has extended the rule to the dog, on the dog's behalf.

His reign has begun 👑. Thou shalt not drink!

The real news: James had surgery this week. Ear tubes, Wednesday morning. He was an absolute trooper. It is Thursday, and you would not have the faintest clue that anything had happened. Great mood, full blabber, back to narrating the apartment at top volume. Kids his age bounce back at a rate that should honestly be studied. We are camped on the Upper East Side this week, so Maddie’s parents can help with him, which has been a genuine gift.

We took a family walk through Central Park this morning. Same but different from our usual Prospect Park. Maddie looked around and said, “Everyone here is such a specific breed.” I assumed she meant the people. She meant the dogs.

Central Park is wall-to-wall purebreds. Prospect Park is wall-to-wall mutts. The people, near as I can tell, are the same in both parks.

They just accessorize differently.

Anyway,

The mistake everyone makes with AI product ads is not taking the genuine time (and it does take a fair amount of time) to ensure their product is right 95% of the time in their ads (sorry, I cannot guarantee 100% given AI drift).

These are things I am sure you are familiar with, like: does the bottle come out with the right label, the right color, the right typography?

Or the composition: does the product look correct in the scene?

When you write one prompt for a beautiful bathroom-counter ad and attach a product photo and hit go, you are gambling.

Usually, the scene lands and the label is subtly wrong, so you re-roll, and now you are re-gambling the label every time you chase a better scene.

The solution? Split the job. Solve the product completely, in isolation, before you touch any ads.

Then generate ads that inherit the locked product. You go from gambling on two things per render to gambling on one.

That is the part I have been thinking about all week. Then the work taught me a second thing I did not expect: that “the locked product” is not a single fixed asset.

The references you need change depending on the ad. And when an ad breaks, the fix is usually a missing reference photo, not a sentence you got wrong.

Let me walk through it.

I picked a hard(ish) product on purpose

I have been using the Jones Road Miracle Balm, our test product, for the last two weeks.

It was locked in three iterations.

Too easy.

One wordmark on a white lid is not enough surface for the model to break. I had not proven anything, which is a polite way of saying I spent an hour rendering a lip balm for no reason except that I like how it comes out.

So I switched to a Prose duo:

Custom Dry Shampoo and Custom Scalp Mask.

Two products, same brand, different shapes, different label colors, same typographic system, and a personalized name printed on each label. The label hierarchy is five stacked elements: a two-line serif product name, a rule, the word FOR in small caps, the personalized name, another rule, and the prose® wordmark in lowercase with a registered mark.

Get any of those five wrong on either product and the ad fails brand QA. Render the name as “Hannah” when the customer is “Hana” and you have broken the one promise the brand is built on, in the customer’s own feed. This is a product worth locking before a single ad exists.

Stage one: dissect, then lock in isolation

I wrote the product understanding into five markdown files in a brand-dna/ folder. The scripts read them at runtime, so changing a rule changes every render after it. The discipline lives in the file, not in the prompt.

The heavy one is 02-products.md. The dissection. Geometry (the dry shampoo is a 2.6-to-1 cylinder, the pump cap is 10 percent wider than the body, the nozzle angles forward about 30 degrees), materials (amber translucent glass, light passes through it, not opaque plastic), the label color as a hex (coral-peach is #E68B73, cream-beige is #E2D7BB), the type family, and the five-element hierarchy spelled out line by line. Writing three hundred words about the angle of a pump nozzle is a strange way to spend a morning, and it feels excessive the whole time you do it. It is not excessive. Every line is a failure mode you are pre-empting, because the model’s defaults for “amber skincare bottle” include clear glass, a sans-serif label, and a helpful tagline you never asked for.

Then the gate. Before generating any ad, I rendered each product alone, on plain cream, three times, and asked the only question that matters at this stage: does it come out correct every time.

All six held the brand-critical elements. The only thing that wobbled was overall proportions, the jar slightly squatter in one, slightly taller in another. Invisible inside a scene. Noted it and moved on. Gate cleared. The product is locked. Now I stop thinking about the product and start thinking about ads.

Stage two: thirteen ads locked. one didn’t.

Fourteen ad types, one generator, same five files, same four references in the same order (canonical photos first, label crops last for highest weight). Only the scene language changes between ads. I batched all fourteen in parallel. Fifteen minutes, about a dollar ten.

Thirteen landed ship-ready on the first render.

Not luck.

The product was already solved, so each ad only had to get the composition right.

The fourteenth broke. The overhead ingredient flatlay. I asked for a strict top-down shot with both products lying on their sides. The model rendered them standing up. Everything else was correct: mint, jojoba beads, charcoal, pink clay, all in the right corners, both labels perfect. The products just would not lie down.

I patched the prompt. Stronger words. “Strict overhead, camera at 90 degrees, not three-quarter.” The dry shampoo lay down. The scalp mask kept standing, like it had not heard me. I patched harder. I tried “top-down.” I tried “lying flat as if it fell over.” I tried “imagine you knocked it on its side.” I was maybe two adjectives away from typing “please” when I realized I was the problem, not the prompt.

The broken ad didn’t need a prompt. It needed a photo.

Here is the insight, and it is the actual point of this whole issue.

Every reference image I had uploaded showed the product standing up at eye level. The canonical photo: upright, eye level. The label crop: upright, eye level. I was asking the model to render a top-down view of a product lying on its side while every reference it had showed the opposite. No quantity of words beats a stack of references all pointing the other way. The model averages what it sees, and everything it saw was vertical.

The product was locked. The asset was not, because this asset needed to see the product from an angle I never gave it.

So the reference set is not one global thing you lock once. It is per ad type. Eye-level ads inherit your eye-level references. An overhead flatlay needs an overhead reference. A low-angle hero shot would need a low-angle reference. The references have to show the product the way the specific ad needs to see it.

I did not have a photo of the Prose products lying down. Nobody does. So I made one.

The part to actually remember

Your reference set is per ad type, not global.

Lock the product once in isolation so fidelity is solved. Then, for each ad type, figure out the minimum set of uploads that renders the product correctly in that specific composition. Most eye-level ads share one baseline set. A handful of off-axis ads each need a specific extra view. When an ad breaks and the product is otherwise correct, do not reach for better adjectives. Look at your uploads and ask whether any of them show the product the way this ad needs to see it. If none do, generate the one you are missing.

The full per-asset reference matrix lives in the recipe download (brand-dna/03-recipe-rules.md). The short version: eye-level assets (hero, vanity, hand-held, model scene, editorial, stat, before-and-after) all inherit the baseline front-plus-label-crop set. Off-axis assets (overhead flatlay, product-on-its-side) each need a generated angle reference. That table is the lookup that turns every future ad into a known quantity instead of a fight.

How to actually use this

One. Pick the one brand you ship the most ads for. Write 02-products.md for its top three SKUs. Twenty minutes per SKU. The file is the asset.

Two. Stage a canonical photo and a tight label crop per SKU. That is your baseline set.

Three. Run stage one. Render each product alone, three times. Do not proceed until it locks every time.

Four. Run your ads. The eye-level ones will mostly inherit the baseline.

Five. For any ad that breaks on orientation, generate the missing angle in isolation, then upload it as a reference for that ad. Re-run.

Six. Write down which ad types need which extra uploads. That table is the most valuable thing you will make this month because it turns every future ad for this brand into a lookup rather than a fight.

TLDR

  • Do not solve product fidelity and composition in the same render. Lock the product in isolation first. Ads then inherit it.

  • Stage one: dissect the product into a markdown file (geometry, materials, hex codes, label hierarchy), then render it alone three times until it is correct every time.

  • Stage two: generate ads. On the Prose duo, thirteen of fourteen locked on the first render.

  • The one that broke was not a fidelity problem. It was an orientation problem. The overhead flatlay needed to see the product lying down, and every reference showed it upright.

  • The reference set is PER AD TYPE, not global. Eye-level ads share a baseline set. Off-axis ads each need a specific extra view.

  • When an ad breaks and the product is otherwise correct, the fix is usually a missing camera angle in your uploads. Generate it in isolation (the model cooperates with no scene to fight), then upload it.

  • Proof: same flatlay, three upload sets. Baseline half-failed. Baseline plus generated side-views fixed it. Words never changed.

Sign-off

Next week I want to map the full angle library for one brand. If eye-level, overhead, and low-hero are the three camera relationships most ads use, then maybe the move is to generate all three reference angles up front, the first time you onboard a product, so no ad ever breaks on orientation again. I want to see if a three-angle reference pack makes the per-ad-type fights disappear entirely.

Reply if you run this on your brand. Send me the ad that broke and the angle you had to generate to fix it. The break teaches me more than the thirteen that worked.

Have a great weekend.

Will

Recommended for you

View all
caret-right