remix-graphic-ad-from-reference
Purpose
Given one reference ad image + a target product + new copy, produce a finished static ad
that keeps the reference's layout and composition but swaps the product and words for the new brand.
This powers the app's "pick a Pinterest ad you like → get the same ad for your product" flow.
It does not invent layouts and it does not regenerate the whole scene from scratch. It reads
the reference, then recreates it with GPT Image 2:
- GPT Image 2 (edit the reference) — ALWAYS use this as the generator. Run it in image-edit
mode on the reference itself to preserve the layout/composition and swap in the new product +
copy. This is the engine for EVERY remix (premium look). FAL slug
fal-ai/gpt-image-1/edit-image.
- HTML overlay (goose-graphics) — NOT a generation engine. It is only an optional FINISHING
step: if GPT bakes garbled/misspelled copy, overlay crisp text on top of the GPT output. Never use
it as the primary generator — a remix must always be generated by GPT Image 2.
Inputs
| Input |
Required |
Notes |
reference_image |
yes |
The ad to recreate (local path or URL). One image. |
product |
yes |
Target product: a clean product render/photo (PNG/webp). Pull the real brand asset; grounding/swapping on it is what keeps the label correct. If the brand asset is a multi-product lineup, crop to the ONE relevant product first — the remix grounds on a single clean product per product_slot (use product_images_needed from the slot map for how many distinct products the layout needs). |
copy_changes |
optional |
If omitted, the agent auto-writes it from the brand pack mapped to the template's slot_map (see Phase 0.5). New headline, benefit callouts, social-proof line, discount/badge text. Keep the reference's structure (same zones), swap the words. |
brand |
recommended |
Palette (hex), font, logo/wordmark, voice — from get_brand_kit / a brand-research pack. |
style_source |
optional |
template (DEFAULT) keeps the reference ad's palette/theme; brand recolours to the brand kit's documented palette. See "Brand grounding" below. The caller sets it (e.g. the user asks to "match my brand colours" → brand); absent → template. |
route_hint |
optional |
Engine override. Default is always gpt_image_2; html is only a text-overlay finishing step, never the generator. |
aspect |
optional |
Inherit from the reference; map to the renderer canvas. Default 4:5 / 1080×1350. |
remix_spec |
optional |
The precomputed spec from the template library (slot_map + gen_prompt + remix_engine). If present, SKIP Phase 0 re-analysis — the slots and prompt are already authored. This is the normal path when remixing a library template. |
remix_mode |
from template |
product (swap a physical product) or ui (SaaS/app ad — swap the app screenshot/UI, NEVER insert a product). Tagged on the template. |
app_screenshot |
for ui mode |
The brand's app/UI screenshot to drop into the device frame (used instead of product when remix_mode:ui). |
Brand grounding — read the kit, never improvise (do this before generating)
The brand context comes from get_brand_kit (structured): colors (palette hex), typography,
products[] (with imageUrls + name/description), screenshotUrls, and referenceImages[]
(each tagged with productName + kind: "product" | "website_screenshot"). Read the full kit, not
a preview.
Palette — by style_source (default template)
Default to template. Use brand ONLY when the user explicitly asks (e.g. "use/match my brand
colours", "make it on-brand"). Do NOT switch to brand on your own judgment — keeping the reference's
look is the expected default.
template (default): keep the reference ad's colours/theme; the brand contributes only its
product image, logo, and copy/voice — NOT its colours.
brand (only on explicit user request): recolour the layout to the brand kit's colors
(primary/accent/bg/text).
- NEVER invent a colour. Every colour in the output must come from EITHER the reference
(
template) OR the kit's documented colors (brand). Do not pull an accent off the logo, a
mascot, a product, or "what looks nice." If style_source: brand but the kit has no palette, fall
back to template (don't guess). This is the #1 off-brand failure.
Which image to feature — select from the kit, send ONLY the relevant one(s)
- Physical-product brand: the hero is the brand's product. Pick from
referenceImages where
productName matches the slot's product and kind is "product" (hero = that product's
imageUrls[0]). Send only that product's image(s) for the slot — not all reference images.
- SaaS / software / app brand (
brandType ∈ saas/software/service/app/platform, or
remix_mode: ui): the hero is the brand's app UI — use referenceImages with
kind: "website_screenshot" (i.e. screenshotUrls). NEVER insert a physical product or a
mascot for a SaaS ad. If there is no screenshot, rebrand the existing UI (recolor/logo/copy).
- Don't substitute a mascot/logo for the product unless the brand genuinely has no product/app image
and the layout's product slot can't be filled — and if so, say so to the user, don't silently swap.
Composed Atoms
goose-graphics (skills/design/composites/goose-graphics) — renders the HTML-overlay path to PNG via the goose-graphics Playwright pipeline.
create-image-gpt-image-fal (skills/ads/capabilities/create-image-gpt-image-fal) — GPT Image 2, run in edit mode on the reference for the photographic path.
create-product-images-higgsfield-product-photoshoot — OPTIONAL: only when no clean product render exists and one must be generated first. Not bundled in goose-skills yet; if absent, require the caller to supply a clean product image.
Workflow
Phase 0 — Get the spec (use remix_spec if it exists; analyze only if it doesn't)
If the template carries a remix_spec (every library template does), DO NOT re-analyze the image.
Read it directly:
remix_spec.slot_map.text_slots → the text fields to replace (each has id, role, and the
reference's current text). Map the brand's copy_changes onto these slots by id/role.
remix_spec.slot_map.product_images_needed + product_slots → how many brand product images to ask
for and where each goes. Cut each out with scripts/cutout_product.py.
remix_spec.slot_map.logo_slots / decorations → logo placement + arrows/badges to reproduce.
remix_spec.gen_prompt → the ready, real-product-locked GPT prompt (fill its {{placeholders}}).
remix_spec.remix_engine → the engine to use (skip Phase 1 routing).
Only if there is no remix_spec (a brand-new, un-tagged reference): view it at full res and write a
one-paragraph anatomy (background, product placement, every text zone, palette, font) — i.e. produce a
remix_spec on the fly. Prefer running the template through the triage tagger first so this is cached.
Phase 0.5 — Author the copy (agent auto-writes; user can revise)
If copy_changes is supplied, use it. Otherwise the agent writes it — read the brand pack
(voice/tone, value-props, never-say) and map onto remix_spec.slot_map.text_slots: one on-brand line
per slot, matching each slot's role and keeping length close to the reference's current text so it
fits the layout. Respect never-say / no-competitor / no-unverifiable-claims. Write one line for every text slot the reference has (no more, no fewer) — these fill the gen_prompt {{text_slots}} block. Add no text the reference doesn't have, drop none it does; if an area has no text in the reference, add none. The output is an exact copy of the reference differing only in product, theme/colour, and the words.
Versioning: show the drafted copy; on user feedback, regenerate a v2 (re-author the copy → re-render)
rather than editing pixels. Keep each version.
Phase 1 — Engine (ALWAYS GPT Image 2)
Always generate with GPT Image 2 (fal-ai/gpt-image-1/edit-image, edit-the-reference mode) — go straight to Phase 2B. Do NOT use the HTML-overlay path as the generator, and ignore remix_spec.remix_engine even if it says html — every remix is generated by GPT Image 2 (this is a billed FAL generation via the proxy). Phase 2A (HTML/goose-graphics) is ONLY an optional text-overlay finishing step if GPT bakes wrong copy.
Phase 2A — HTML-overlay (OPTIONAL text-overlay finishing only — NOT the generator)
- Cut the product out to a transparent PNG with
scripts/cutout_product.py (handles palette-transparency
renders and white/solid backgrounds via edge flood-fill — preserves interior white logos).
- Build one
index.html from assets/overlay-template.html: reproduce the reference's background,
place the cutout, lay the new copy in the same zones. Arrows = inline SVG; scalloped seals = inline JS
(template has both). Use the brand font (Google Fonts) + palette.
- Render via
create-goose-graphics / the goose-graphics screenshot pipeline:
node <goose-graphics>/screenshot/screenshot.js --format <canvas> --input index.html --output render.png --font-delay 1500.
Map aspect→canvas: 1080×1080→carousel, 1080×1350→poster, 1080×1920→story. 2:3 (1080×1620) has NO goose-graphics preset → render at poster (1080×1350, nearest) or a standalone 2:3 Playwright canvas; don't hand-edit the vendored renderer.
Phase 2B — GPT Image 2 path (EDIT the reference, do not regenerate)
- Run GPT Image 2 in image-edit mode with the reference ad as the base + the product render —
prompt it to keep the reference's layout, background, camera, lighting, and signature element exactly
and only (a) replace the product with the attached one (match its label/logo exactly) and
(b) clear the original text.
⚠️ Never generate grounded on the product alone — that invents a generic new scene and discards
the reference's composition.
- Text: either let GPT render the new SHORT copy, or have it leave clean space and add copy via
the HTML overlay. Never stack an HTML text layer on output that already has baked text — one source.
--aspect_ratio 3:4 (or nearest), --quality high --resolution 2k. If the model returns a small
image (long edge < ~1080px), upscale to ≥2k before shipping — AI-path baked text softens at low res
(a known tradeoff vs the HTML path, which is natively 2×/crisp). GPT edit slug: fal-ai/gpt-image-1/edit-image (image_urls[]); it caps ~1536px long edge, so upscale to ≥2k via fal-ai/esrgan.
- Fidelity gate (3 checks, all must pass): (a) signature element/layout preserved; (b) EVERY instance
of the source product is replaced and NO source/competitor brand name, logo, or label survives anywhere
in frame (diff full-res output vs reference — a partial swap that leaves a competitor bottle is a brand/
compliance hazard); (c) output is full-res (long edge ≥ ~2000px) and baked text is sharp. If any fails:
re-roll GPT on the original reference, or (for text-only failures) overlay the text via goose-graphics
on top of the GPT image. Reject only if GPT can't preserve the layout at all — never swap to HTML as
the generator.
Phase 3 — QC + deliver
Run Quality Checks. Save the master to finals/; keep HTML/cutout/prompt + a provenance note in
working/. If copy is wrong on a GPT output, switch that ad to the HTML path rather than re-rolling text.
Decision Rules
The engine is ALWAYS GPT Image 2 — there is no routing decision. Generate every remix with
fal-ai/gpt-image-1/edit-image in edit-the-reference mode (Phase 2B). The rules below are about
how to run GPT well, not whether to use it.
- GPT edits the reference, never regenerates from the product alone. Keep the reference's
layout, background, camera, and signature element; only swap the product + clear the old text.
- SaaS/app/software ad →
remix_mode: ui. No physical product — swap the app screenshot/UI
into the device frame and rebrand; NEVER insert a physical product. If no app screenshot,
rebrand the existing UI (recolor/logo/copy). Use the UI gen_prompt on the template.
- If GPT bakes garbled/misspelled copy or a wrong price/claim: re-roll GPT with an explicit
"render this exact text" prompt; if it still fails, keep the GPT image but overlay the crisp
text on top via goose-graphics (Phase 2A) as a finishing step. Do NOT fall back to HTML as the
generator — the base image is always GPT.
- No fear / no competitor-bashing / no unverifiable claims, even if the reference uses them. Re-skin the hook.
- Bespoke cinematic / IP-bound references (specific VFX device, celebrity, brand character) are not remixable — reject.
Output
finals/<slug>_<WxH>.png — the finished ad (2× device scale on the HTML path → e.g. 2160×2700).
working/index.html + working/<product>-cutout.png (HTML path) — editable source for copy/price variants.
working/ai-gen/gpt-v1.png + working/ai-gen/PROVENANCE.md (GPT path) — URL, model, prompt, credits.
- A one-line route + cost record.
Quality Checks
Read the real brand product asset next to the full-res generated output and compare them
directly — most AI-path failures are product drift you can't catch without the side-by-side:
- Product shape & size: the same product at the right proportions and scale — not stretched,
squashed, bent, oddly cropped, or resized out of proportion vs. the real asset.
- Product colour: colours match the real asset — body, cap, packaging, finish, AND any visible
contents (pill/powder/liquid); no hue shift, wrong shade, or washed-out / oversaturated look. (See
the "Product internals inherit the reference colour" failure mode.)
- Text not garbled: every word on the product label/packaging and in the ad copy is real,
correctly spelled, and legible — not garbled, melted, doubled, or invented. Discount/price/claim ==
the intended value.
- Logo: if any logo / wordmark appears in the frame, it must be the brand's CORRECT logo — right
mark, right spelling, undistorted, correct colours. A wrong or mangled logo = fail.
- Layout matches the reference anatomy: same zones, reading order, energy. No colliding text; legible
at thumbnail size.
- Brand alignment (per
style_source): colours come from the right source — the reference's
palette for template, the kit's documented colors for brand. No invented / off-brand
colour (e.g. an accent pulled off a mascot or logo — the #1 failure). Typography = the kit's
fonts. The featured asset is the brand's real product (or app UI for SaaS), selected from
the kit — never a mascot/placeholder standing in for the product.
- View the full-res output, not a thumbnail — text errors hide at small sizes.
If any product / text / logo check fails, re-roll the same engine on the original reference with an
explicit prompt ("keep the product's exact shape, proportions and colours; render the label text and
logo exactly as in the attached product image; do not distort or restyle them"), or route text/logo-heavy
cards to the HTML path (crisp text + the real logo file placed directly). Don't ship a render whose
product is the wrong size or colour, has garbled text, or shows a wrong / distorted logo.
Failure Modes
- AI "changes everything" / drops the reference's signature element. Cause: generating grounded on
the product alone. Fix: GPT must edit the reference. If it still can't preserve the layout, use HTML
or reject the reference.
- Partial swap / residual competitor branding. The AI replaced only one of several products and left a
source/competitor bottle, dropper, logo, or label in frame — a brand/compliance hazard, not just a
fidelity miss. The gen_prompt now says "replace EVERY instance, remove ALL source branding"; still
verify it in the fidelity gate by diffing against the reference.
- Product internals inherit the reference (wrong pill/powder/liquid colour). GPT can keep the
reference product's capsule/powder colour instead of the brand's. Fix: the gen_prompt says match the
brand image's visible contents — so the brand product image must SHOW the real contents. If the brand's
actual pill/powder/liquid colour isn't in the research pack or asset, ASK the user — don't guess.
- Hand-held product → hallucinated hand. Templates where a hand holds/grips the product hallucinate a malformed hand on GPT-edit (HTML can't render a hand either). Only replicable if a real product-in-hand photo is supplied; otherwise reject the template or pick a non-hand layout.
- Stacking two text layers (baked text + HTML overlay) = doubled/conflicting copy. Use ONE text source.
- Bespoke/cinematic/IP references aren't remixable by product+copy swap — reject, don't attempt.
- GPT bakes wrong copy/discount. Don't re-roll more than once for text — overlay the correct text via goose-graphics (Phase 2A) on top of the GPT image; keep GPT as the base.
- Cutout eats the white logo. Use edge flood-fill (the helper does this), never a global white→transparent threshold.
- Product asset is the back of the pack / wrong SKU. Verify the face before using it.
Spend Reference
At ~$1 = 21 Higgsfield credits:
- GPT Image 2 (3:4, 2k high): ~7 cr ≈ $0.33 per image — this is the path EVERY remix uses.
- HTML-overlay (goose-graphics): $0 — only the optional text-overlay finishing step, not a remix on its own.
Research/setup dominates the agent cost for the first ad of a new brand; variants are far cheaper.
Tests
See tests/. Smoke = route + render a known reference→product pair end-to-end and confirm a non-empty
PNG at the right dimensions with correct copy. Verifier: skills/ads/capabilities/verify-product-image/.
Skill Location & Related
skills/ads/composites/remix-graphic-ad-from-reference/ (goose-skills). Related:
skills/ads/composites/brand-research (brand context), skills/ads/capabilities/create-image-gpt-image-fal
(GPT Image 2 edit engine), skills/ads/capabilities/verify-product-image (QC), and
skills/design/composites/goose-graphics (HTML→PNG renderer). The video analog remix-ad lives in the
separate ads-video repo.
1---2name: remix-graphic-ad-from-reference3description: Recreate a static graphic ad (Pinterest pin, IG/FB feed image, poster) from a reference image, swapping in a new brand's product and new copy while keeping the reference's layout, composition, and visual energy. ALWAYS generated with GPT Image 2 in edit-the-reference mode (fal-ai/gpt-image-1/edit-image, a billed FAL generation); the HTML/goose-graphics overlay is only an optional text-finishing step, never the generator. The static-graphics counterpart to the video remix-ad skill; this is what the app calls when a user picks a reference ad and wants it for their own product.4---5
6# remix-graphic-ad-from-reference
7
8## Purpose
9
10Given **one reference ad image** + a **target product** + **new copy**, produce a finished static ad
11that keeps the reference's *layout and composition* but swaps the product and words for the new brand.
12This powers the app's "pick a Pinterest ad you like → get the same ad for your product" flow.
13
14It does **not** invent layouts and it does **not** regenerate the whole scene from scratch. It reads
15the reference, then recreates it with GPT Image 2:
16
17- **GPT Image 2 (edit the reference) — ALWAYS use this as the generator.** Run it in **image-edit
18 mode on the reference itself** to preserve the layout/composition and swap in the new product +
19 copy. This is the engine for EVERY remix (premium look). FAL slug `fal-ai/gpt-image-1/edit-image`.
20- **HTML overlay (goose-graphics) — NOT a generation engine.** It is only an optional FINISHING
21 step: if GPT bakes garbled/misspelled copy, overlay crisp text on top of the GPT output. Never use
22 it as the primary generator — a remix must always be generated by GPT Image 2.
23
24
25## Inputs
26
27| Input | Required | Notes |
28|---|---|---|
29| `reference_image` | yes | The ad to recreate (local path or URL). One image. |
30| `product` | yes | Target product: a clean product render/photo (PNG/webp). Pull the **real** brand asset; grounding/swapping on it is what keeps the label correct. **If the brand asset is a multi-product lineup, crop to the ONE relevant product first** — the remix grounds on a single clean product per `product_slot` (use `product_images_needed` from the slot map for how many distinct products the layout needs). |
31| `copy_changes` | optional | If omitted, **the agent auto-writes it** from the brand pack mapped to the template's `slot_map` (see Phase 0.5). New headline, benefit callouts, social-proof line, discount/badge text. Keep the reference's *structure* (same zones), swap the words. |
32| `brand` | recommended | Palette (hex), font, logo/wordmark, voice — from `get_brand_kit` / a `brand-research` pack. |
33| `style_source` | optional | **`template` (DEFAULT)** keeps the reference ad's palette/theme; **`brand`** recolours to the brand kit's documented palette. See "Brand grounding" below. The caller sets it (e.g. the user asks to "match my brand colours" → `brand`); absent → `template`. |
34| `route_hint` | optional | Engine override. Default is **always `gpt_image_2`**; `html` is only a text-overlay finishing step, never the generator. |
35| `aspect` | optional | Inherit from the reference; map to the renderer canvas. Default 4:5 / 1080×1350. |
36| `remix_spec` | optional | The precomputed spec from the template library (`slot_map` + `gen_prompt` + `remix_engine`). **If present, SKIP Phase 0 re-analysis** — the slots and prompt are already authored. This is the normal path when remixing a library template. |
37| `remix_mode` | from template | `product` (swap a physical product) or `ui` (SaaS/app ad — swap the app screenshot/UI, NEVER insert a product). Tagged on the template. |
38| `app_screenshot` | for `ui` mode | The brand's app/UI screenshot to drop into the device frame (used instead of `product` when `remix_mode:ui`). |
39
40## Brand grounding — read the kit, never improvise (do this before generating)
41
42The brand context comes from `get_brand_kit` (structured): `colors` (palette hex), `typography`,
43`products[]` (with `imageUrls` + `name`/`description`), `screenshotUrls`, and `referenceImages[]`
44(each tagged with `productName` + `kind: "product" | "website_screenshot"`). **Read the full kit, not
45a preview.**
46
47### Palette — by `style_source` (default `template`)
48**Default to `template`. Use `brand` ONLY when the user explicitly asks** (e.g. "use/match my brand
49colours", "make it on-brand"). Do NOT switch to `brand` on your own judgment — keeping the reference's
50look is the expected default.
51- **`template` (default):** keep the **reference ad's** colours/theme; the brand contributes only its
52 product image, logo, and copy/voice — NOT its colours.
53- **`brand` (only on explicit user request):** recolour the layout to the brand kit's `colors`
54 (primary/accent/bg/text).
55- **NEVER invent a colour.** Every colour in the output must come from EITHER the reference
56 (`template`) OR the kit's documented `colors` (`brand`). Do **not** pull an accent off the logo, a
57 mascot, a product, or "what looks nice." If `style_source: brand` but the kit has no palette, fall
58 back to `template` (don't guess). This is the #1 off-brand failure.
59
60### Which image to feature — select from the kit, send ONLY the relevant one(s)
61- **Physical-product brand:** the hero is the brand's product. Pick from `referenceImages` where
62 `productName` matches the slot's product and `kind` is `"product"` (hero = that product's
63 `imageUrls[0]`). Send only that product's image(s) for the slot — **not** all reference images.
64- **SaaS / software / app brand** (`brandType` ∈ saas/software/service/app/platform, or
65 `remix_mode: ui`): the hero is the brand's **app UI** — use `referenceImages` with
66 `kind: "website_screenshot"` (i.e. `screenshotUrls`). **NEVER insert a physical product or a
67 mascot** for a SaaS ad. If there is no screenshot, rebrand the existing UI (recolor/logo/copy).
68- Don't substitute a mascot/logo for the product unless the brand genuinely has no product/app image
69 and the layout's product slot can't be filled — and if so, say so to the user, don't silently swap.
70
71## Composed Atoms
72
73- `goose-graphics` (`skills/design/composites/goose-graphics`) — renders the HTML-overlay path to PNG via the goose-graphics Playwright pipeline.
74- `create-image-gpt-image-fal` (`skills/ads/capabilities/create-image-gpt-image-fal`) — GPT Image 2, run in **edit mode on the reference** for the photographic path.
75- `create-product-images-higgsfield-product-photoshoot` — OPTIONAL: only when no clean product render exists and one must be generated first. Not bundled in goose-skills yet; if absent, require the caller to supply a clean product image.
76
77## Workflow
78
79### Phase 0 — Get the spec (use `remix_spec` if it exists; analyze only if it doesn't)
80**If the template carries a `remix_spec` (every library template does), DO NOT re-analyze the image.**
81Read it directly:
82- `remix_spec.slot_map.text_slots` → the text fields to replace (each has `id`, `role`, and the
83 reference's current text). Map the brand's `copy_changes` onto these slots by `id`/`role`.
84- `remix_spec.slot_map.product_images_needed` + `product_slots` → how many brand product images to ask
85 for and where each goes. Cut each out with `scripts/cutout_product.py`.
86- `remix_spec.slot_map.logo_slots` / `decorations` → logo placement + arrows/badges to reproduce.
87- `remix_spec.gen_prompt` → the ready, real-product-locked GPT prompt (fill its `{{placeholders}}`).
88- `remix_spec.remix_engine` → the engine to use (skip Phase 1 routing).
89
90**Only if there is no `remix_spec`** (a brand-new, un-tagged reference): view it at full res and write a
91one-paragraph anatomy (background, product placement, every text zone, palette, font) — i.e. produce a
92`remix_spec` on the fly. Prefer running the template through the triage tagger first so this is cached.
93
94### Phase 0.5 — Author the copy (agent auto-writes; user can revise)
95If `copy_changes` is supplied, use it. **Otherwise the agent writes it** — read the brand pack
96(voice/tone, value-props, never-say) and map onto `remix_spec.slot_map.text_slots`: one on-brand line
97per slot, matching each slot's `role` and keeping length close to the reference's current text so it
98fits the layout. Respect never-say / no-competitor / no-unverifiable-claims. Write one line for **every text slot the reference has** (no more, no fewer) — these fill the `gen_prompt` `{{text_slots}}` block. **Add no text the reference doesn't have, drop none it does**; if an area has no text in the reference, add none. The output is an exact copy of the reference differing only in product, theme/colour, and the words.
99**Versioning:** show the drafted copy; on user feedback, regenerate a **v2** (re-author the copy → re-render)
100rather than editing pixels. Keep each version.
101
102### Phase 1 — Engine (ALWAYS GPT Image 2)
103**Always generate with GPT Image 2** (`fal-ai/gpt-image-1/edit-image`, edit-the-reference mode) — go straight to **Phase 2B**. Do NOT use the HTML-overlay path as the generator, and **ignore `remix_spec.remix_engine` even if it says `html`** — every remix is generated by GPT Image 2 (this is a billed FAL generation via the proxy). Phase 2A (HTML/goose-graphics) is ONLY an optional text-overlay finishing step if GPT bakes wrong copy.
104
105### Phase 2A — HTML-overlay (OPTIONAL text-overlay finishing only — NOT the generator)
1061. Cut the product out to a transparent PNG with `scripts/cutout_product.py` (handles palette-transparency
107 renders *and* white/solid backgrounds via edge flood-fill — preserves interior white logos).
1082. Build one `index.html` from `assets/overlay-template.html`: reproduce the reference's background,
109 place the cutout, lay the new copy in the same zones. Arrows = inline SVG; scalloped seals = inline JS
110 (template has both). Use the brand font (Google Fonts) + palette.
1113. Render via `create-goose-graphics` / the goose-graphics screenshot pipeline:
112 `node <goose-graphics>/screenshot/screenshot.js --format <canvas> --input index.html --output render.png --font-delay 1500`.
113 Map aspect→canvas: 1080×1080→`carousel`, 1080×1350→`poster`, 1080×1920→`story`. **2:3 (1080×1620) has NO goose-graphics preset** → render at `poster` (1080×1350, nearest) or a standalone 2:3 Playwright canvas; don't hand-edit the vendored renderer.
114
115### Phase 2B — GPT Image 2 path (EDIT the reference, do not regenerate)
1161. **Run GPT Image 2 in image-edit mode with the reference ad as the base + the product render** —
117 prompt it to *keep the reference's layout, background, camera, lighting, and signature element exactly*
118 and only **(a)** replace the product with the attached one (match its label/logo exactly) and
119 **(b)** clear the original text.
120 ⚠️ **Never generate grounded on the product alone** — that invents a generic new scene and discards
121 the reference's composition.
1222. **Text:** either let GPT render the new SHORT copy, **or** have it leave clean space and add copy via
123 the HTML overlay. **Never stack an HTML text layer on output that already has baked text** — one source.
1243. `--aspect_ratio 3:4` (or nearest), `--quality high --resolution 2k`. **If the model returns a small
125 image (long edge < ~1080px), upscale to ≥2k before shipping** — AI-path baked text softens at low res
126 (a known tradeoff vs the HTML path, which is natively 2×/crisp). GPT edit slug: `fal-ai/gpt-image-1/edit-image` (`image_urls[]`); it caps ~1536px long edge, so upscale to ≥2k via `fal-ai/esrgan`.
1274. **Fidelity gate (3 checks, all must pass):** (a) signature element/layout preserved; (b) **EVERY instance
128 of the source product is replaced and NO source/competitor brand name, logo, or label survives anywhere**
129 in frame (diff full-res output vs reference — a partial swap that leaves a competitor bottle is a brand/
130 compliance hazard); (c) output is full-res (long edge ≥ ~2000px) and baked text is sharp. If any fails:
131 re-roll GPT on the original reference, or (for text-only failures) overlay the text via goose-graphics
132 on top of the GPT image. Reject only if GPT can't preserve the layout at all — never swap to HTML as
133 the generator.
134
135### Phase 3 — QC + deliver
136Run Quality Checks. Save the master to `finals/`; keep HTML/cutout/prompt + a provenance note in
137`working/`. If copy is wrong on a GPT output, switch that ad to the HTML path rather than re-rolling text.
138
139## Decision Rules
140
141**The engine is ALWAYS GPT Image 2 — there is no routing decision.** Generate every remix with
142`fal-ai/gpt-image-1/edit-image` in edit-the-reference mode (Phase 2B). The rules below are about
143how to run GPT well, not whether to use it.
144
145- **GPT edits the reference, never regenerates from the product alone.** Keep the reference's
146 layout, background, camera, and signature element; only swap the product + clear the old text.
147- **SaaS/app/software ad → `remix_mode: ui`.** No physical product — swap the **app screenshot/UI**
148 into the device frame and rebrand; **NEVER insert a physical product**. If no app screenshot,
149 rebrand the existing UI (recolor/logo/copy). Use the UI `gen_prompt` on the template.
150- **If GPT bakes garbled/misspelled copy or a wrong price/claim:** re-roll GPT with an explicit
151 "render this exact text" prompt; if it still fails, keep the GPT image but **overlay the crisp
152 text on top via goose-graphics (Phase 2A)** as a finishing step. Do NOT fall back to HTML as the
153 generator — the base image is always GPT.
154- **No fear / no competitor-bashing / no unverifiable claims**, even if the reference uses them. Re-skin the hook.
155- **Bespoke cinematic / IP-bound references** (specific VFX device, celebrity, brand character) are not remixable — reject.
156
157## Output
158
159- `finals/<slug>_<WxH>.png` — the finished ad (2× device scale on the HTML path → e.g. 2160×2700).
160- `working/index.html` + `working/<product>-cutout.png` (HTML path) — editable source for copy/price variants.
161- `working/ai-gen/gpt-v1.png` + `working/ai-gen/PROVENANCE.md` (GPT path) — URL, model, prompt, credits.
162- A one-line route + cost record.
163
164## Quality Checks
165
166**`Read` the real brand product asset next to the full-res generated output and compare them
167directly** — most AI-path failures are product drift you can't catch without the side-by-side:
168
169- **Product shape & size:** the same product at the right proportions and scale — not stretched,
170 squashed, bent, oddly cropped, or resized out of proportion vs. the real asset.
171- **Product colour:** colours match the real asset — body, cap, packaging, finish, AND any visible
172 contents (pill/powder/liquid); no hue shift, wrong shade, or washed-out / oversaturated look. (See
173 the "Product internals inherit the reference colour" failure mode.)
174- **Text not garbled:** every word on the product label/packaging and in the ad copy is real,
175 correctly spelled, and legible — not garbled, melted, doubled, or invented. Discount/price/claim ==
176 the intended value.
177- **Logo:** if any logo / wordmark appears in the frame, it must be the brand's CORRECT logo — right
178 mark, right spelling, undistorted, correct colours. A wrong or mangled logo = fail.
179- Layout matches the reference anatomy: same zones, reading order, energy. No colliding text; legible
180 at thumbnail size.
181- **Brand alignment (per `style_source`):** colours come from the right source — the **reference's**
182 palette for `template`, the kit's documented **`colors`** for `brand`. **No invented / off-brand
183 colour** (e.g. an accent pulled off a mascot or logo — the #1 failure). Typography = the kit's
184 fonts. The featured asset is the brand's **real product** (or **app UI** for SaaS), selected from
185 the kit — never a mascot/placeholder standing in for the product.
186- View the **full-res** output, not a thumbnail — text errors hide at small sizes.
187
188If any product / text / logo check fails, re-roll the same engine on the **original reference** with an
189explicit prompt ("keep the product's exact shape, proportions and colours; render the label text and
190logo exactly as in the attached product image; do not distort or restyle them"), or route text/logo-heavy
191cards to the HTML path (crisp text + the real logo file placed directly). Don't ship a render whose
192product is the wrong size or colour, has garbled text, or shows a wrong / distorted logo.
193
194## Failure Modes
195
196- **AI "changes everything" / drops the reference's signature element.** Cause: generating grounded on
197 the product alone. Fix: GPT must **edit the reference**. If it still can't preserve the layout, use HTML
198 or reject the reference.
199- **Partial swap / residual competitor branding.** The AI replaced only one of several products and left a
200 source/competitor bottle, dropper, logo, or label in frame — a brand/compliance hazard, not just a
201 fidelity miss. The gen_prompt now says "replace EVERY instance, remove ALL source branding"; still
202 verify it in the fidelity gate by diffing against the reference.
203- **Product internals inherit the reference (wrong pill/powder/liquid colour).** GPT can keep the
204 *reference* product's capsule/powder colour instead of the brand's. Fix: the gen_prompt says match the
205 brand image's visible contents — so the brand product image must SHOW the real contents. If the brand's
206 actual pill/powder/liquid colour isn't in the research pack or asset, **ASK the user — don't guess.**
207- **Hand-held product → hallucinated hand.** Templates where a hand holds/grips the product hallucinate a malformed hand on GPT-edit (HTML can't render a hand either). Only replicable if a real product-in-hand photo is supplied; otherwise reject the template or pick a non-hand layout.
208- **Stacking two text layers** (baked text + HTML overlay) = doubled/conflicting copy. Use ONE text source.
209- **Bespoke/cinematic/IP references** aren't remixable by product+copy swap — reject, don't attempt.
210- **GPT bakes wrong copy/discount.** Don't re-roll more than once for text — overlay the correct text via goose-graphics (Phase 2A) on top of the GPT image; keep GPT as the base.
211- **Cutout eats the white logo.** Use edge flood-fill (the helper does this), never a global white→transparent threshold.
212- **Product asset is the back of the pack / wrong SKU.** Verify the face before using it.
213
214## Spend Reference
215
216At ~$1 = 21 Higgsfield credits:
217- GPT Image 2 (3:4, 2k high): **~7 cr ≈ $0.33** per image — this is the path EVERY remix uses.
218- HTML-overlay (goose-graphics): **$0** — only the optional text-overlay finishing step, not a remix on its own.
219Research/setup dominates the agent cost for the first ad of a new brand; variants are far cheaper.
220
221## Tests
222
223See `tests/`. Smoke = route + render a known reference→product pair end-to-end and confirm a non-empty
224PNG at the right dimensions with correct copy. Verifier: `skills/ads/capabilities/verify-product-image/`.
225
226## Skill Location & Related
227
228`skills/ads/composites/remix-graphic-ad-from-reference/` (goose-skills). Related:
229`skills/ads/composites/brand-research` (brand context), `skills/ads/capabilities/create-image-gpt-image-fal`
230(GPT Image 2 edit engine), `skills/ads/capabilities/verify-product-image` (QC), and
231`skills/design/composites/goose-graphics` (HTML→PNG renderer). The video analog `remix-ad` lives in the
232separate ads-video repo.