Every Nano Banana version run through the production channel-thumbnail module — buildThumbBrief + applyThumbnailChannelIdentity + golden craft bar + owner A/B preferences — on three different channel types. Byte-identical prompt per channel across all four models.
Generated 2026-09-03 · 12 images · $0.8498 total spend.
$0.134 per 1K image for Pro, but the API's candidatesTokenCount came back at ~1460, not 1120. The extra ~340 are thinking/text tokens billed at $12/1M, not the $120/1M image rate. Bill on candidatesTokenCount and you overstate Pro by ~30%. The real all-in is $0.1407 — image tokens plus a small thinking charge.| API model | Marketing name | Batch 1K | Standard 1K | 2K | 4K |
|---|---|---|---|---|---|
gemini-3.1-flash-lite-image | Nano Banana 2 Lite | $0.0168 | $0.0336 | — | — |
gemini-2.5-flash-image | Nano Banana (1) | n/a | $0.039 | — | — |
gemini-3.1-flash-image | Nano Banana 2 | $0.034 | $0.067 | $0.101 | $0.151 |
gemini-3-pro-image | Nano Banana Pro | $0.067 | $0.134 | $0.134 | $0.240 |
Nano Banana 2 also has a 0.5K tier: $0.045 standard / $0.022 batch. Nano Banana (1) is billed at 1290 tokens per ≤1024² image; every Gemini 3.x image model is 1120 tokens at both 1K and 2K — which is why NB2 Lite undercuts NB1 despite sharing the same $30/1M rate.
| Provider | Model | Posted price | Notes | |
|---|---|---|---|---|
| OpenRouter | NB Pro / NB2 / NB2 Lite | $0.134 / $0.067 / $0.0336 | Verified live via their /api/v1/models endpoint — token rates are identical to Google's ($120 / $60 / $30 per 1M image tokens). Zero markup, one key for everything. | verified |
| Google Batch API | all Nano Banana models | 50% of standard | First-party. Async, up to 24h turnaround. NB2 Lite batch = $0.0168/img is the cheapest legitimate Nano Banana anywhere. | verified |
| kie.ai | NB Pro | $0.09 (1K–2K), $0.12 (4K) | Posted on kie.ai/nano-banana-pro. Credit-based (18 credits ≈ $0.09). Genuine 33% undercut of Google's own rate — the cheapest credible NB Pro reseller. | verified |
| kie.ai | NB2 | $0.04 (1K), $0.06 (2K), $0.09 (4K) | Same platform, NB2 tier. | reported |
| Replicate | NB Pro | $0.14 (1K–2K), $0.24 (4K) | Model JSON exposes no pricing field; figure is from kie.ai's comparison table. Roughly official rate. | reported |
| fal.ai | NB Pro | $0.15 (1K–2K), $0.30 (4K) | Not on fal's public pricing page (only 'Nanobanana $0.0398' is listed). $0.15 is the value pinned in this repo's own sealed contract. Most expensive endpoint in the survey. | reported |
| fal.ai | NB (1) | $0.0398 | Listed on fal.ai/pricing. Essentially official rate. | verified |
| WaveSpeed AI | NB Pro | $0.14 | Aggregator listing only. | reported |
| Together AI / Toapis | NB Pro / NB2 | $0.134 / $0.0466 | Mirror official serverless rates, no markup. | reported |
| Muapi | NB Pro | $0.12 | Aggregator listing only. | reported |
These are relay/gateway platforms quoting prices below what Google charges them. None were verifiable at source; several are promoted through SEO content farms that cite each other.
| Platform | Claimed | Why it doesn't add up |
|---|---|---|
| Picsart | $0.013 flat (NB2 and NB Pro) | Would be 10× under Google's own cost for Pro. Almost certainly a promo/credit-bundle rate or a mislabelled model, not sustainable Pro inference. |
| ApiPass | $0.0136 (NB2 1K) / $0.0864 (NB Pro 1K) | NB Pro figure is plausible-ish; the NB2 figure is below Google's wholesale. |
| laozhang.ai | $0.05 flat, all resolutions | China-facing relay. Alipay/TG payment, 'no VPN needed'. Below Google cost at 2K+. |
| APIYI | $0.05 flat | Same relay class, same claim. |
| Evolink.ai | $0.025–$0.05 | Same relay class. |
| PoYo | $0.12 (NB Pro 1K–2K) | Credit reseller. |
| Apertis | $0.50 flat per NB Pro request | Most expensive in the survey; sells free caching + free grounding instead. |
~$0.0039/image and NB Pro at ~$0.0161. I pulled OpenRouter's live model endpoint: its image-output rates are $0.00006 and $0.00012 per token — exactly Google's $60/$30/$120 per 1M. At 1120 tokens that is $0.067 and $0.134, not $0.0039. That article divided by the wrong token count. Its other numbers deserve the same suspicion.“The £40,000 Pension Mistake Nobody Warns You About”
Rules: 1280x720 YouTube thumbnail. The hero fills 55-75% of the frame, aggressively cropped. Typography is HUGE (owns 25-40% of the frame), ultra-bold, rendered as a designed physical object (plate, smear, strip, slab or sticker) made of the scene's material world, with one PAYOFF word 2-4x larger than the rest. HARD RULE: text NEVER covers the hero's face or eyes - beside, above or across the body only. Spelling EXACTLY as quoted - every visible word must be a correctly spelled real word. Everything must read at 120px on a phone. No play buttons, no UI, no watermarks, no extra small text. Channel "INVESTORY" (signature look, obey strictly: premium realistic financial editorial photograph, tactile real-world materials, decisive human-scale consequence, restrained navy-black and gold grade, palette #0B1220 / #D8A11A, accent #D8A11A). Scene: LAYOUT MODE: split composition; hero opposite the chosen type zone. HERO PROP (dominant, 30-50% of frame, cropped close): a man in his late fifties at a kitchen table, one hand pressed flat on a printed pension statement while the other tears the corner of the page away, his face lit hard from a low window, jaw set in the instant he understands the number. BACKGROUND (separate supporting layer behind the hero - darker, simpler, depth): an out-of-focus suburban kitchen sinking into navy shadow, a cold grey morning beyond the blinds. STORY DETAILS (symbolic, on/around the hero): the torn corner of the statement falling as a thin gold-lit sliver; a cooling mug of tea with an untouched skin on it. Headline: "£40,000" (the payoff word, HUGE) (accent color) then "GONE QUIETLY" - placed clear of all faces. Render the headline as cinematic title-card lettering with metallic bevel and rim light, embedded in the scene atmosphere, blockbuster one-sheet gravity. Small badge pill "INVESTORY" in a corner away from the text. CHANNEL IDENTITY CONTRACT (non-negotiable; reject a scene that misses any visible fact): MUST SHOW: - A believable adult human decision or tactile financial artifact visibly enacts the title's exact wealth mechanism. - The scene still communicates a concrete financial consequence when the headline is covered. - The dominant material reads as photographed paper, property, cash-flow object, or human-scale evidence rather than a digital asset. MUST NOT SHOW: - Generic floating coins, neon market charts, holographic dashboards, anonymous gold bars, luxury filler, video-game art, or a glossy 3D product render. USER-APPROVED GOLDEN CRAFT BAR: One unmistakable, story-specific hero dominates at phone size and is cropped decisively at the frame edge; a centered hero is optional only when the surrounding type and action read more strongly that way. Use only two or three meaningful visual elements with real foreground, hero, and background depth. Avoid a dead 50/50 picture-and-copy split: one hero contour or atmospheric layer should overlap the future type zone. Headline typography is oversized and treated as a physical visual object, never a generic clean font floating in empty space. Keep one forceful contrast system and one restrained accent; reject muddy, generic, or product-render imagery. Channel identity is compact and secondary in a corner; it must never compete with the hook. OWNER-SELECTED A/B PREFERENCES: Prefer a two-word concrete tension or payoff hook over a longer sentence; make the decisive noun visibly dominant. Stage the hero at the peak of action with readable emotion in face, eyes, or hands—not a calm pose after the event. Show cause and consequence together: one close hero action plus one smaller proof detail that completes the story. Bring one consequential hand, object, rupture, or release into the near foreground so the viewer feels the action before decoding the subject. Use a strong diagonal or edge crop to pull the eye from hook to hero to consequence in under one second. Allow a centered hero only for a genuinely stronger peak-action image; arrange supporting type around its silhouette and never turn it into a default symmetrical poster. Do not let a detached signboard or flat black text panel become the composition. Typography must share the scene's material, light, motion, or negative space while leaving the hero dominant. Keep headline contrast immediate and physical while preserving one clean hierarchy; do not let background lore compete.
“The Night Rome's Treasury Vanished”
Rules: 1280x720 YouTube thumbnail. The hero fills 55-75% of the frame, aggressively cropped. Typography is HUGE (owns 25-40% of the frame), ultra-bold, rendered as a designed physical object (plate, smear, strip, slab or sticker) made of the scene's material world, with one PAYOFF word 2-4x larger than the rest. HARD RULE: text NEVER covers the hero's face or eyes - beside, above or across the body only. Spelling EXACTLY as quoted - every visible word must be a correctly spelled real word. Everything must read at 120px on a phone. No play buttons, no UI, no watermarks, no extra small text. Channel "INKED HISTORIES" (signature look, obey strictly: original high-craft historical ink-and-charcoal editorial illustration on aged paper, tactile cross-hatching, dramatic chiaroscuro and restrained ember-gold proof accents, palette #141110 / #C2761F, accent #C2761F). Scene: LAYOUT MODE: split composition; hero opposite the chosen type zone. HERO PROP (dominant, 30-50% of frame, cropped close): a Roman treasury guard wrenching open an iron-banded strongbox at the peak of the discovery, the lid flung back, his lantern arm thrown wide, mouth open in the half-second before he shouts, the box interior utterly empty. BACKGROUND (separate supporting layer behind the hero - darker, simpler, depth): a vaulted stone undercroft receding into deep cross-hatched blackness, one collapsed shelf, torchlight raking the columns. STORY DETAILS (symbolic, on/around the hero): a single coin still spinning on the flagstones catching an ember-gold highlight; a snapped chain swinging from the strongbox hasp. Headline: "EMPTY" (the payoff word, HUGE) (accent color) then "BY DAWN" - placed clear of all faces. Render the headline as cinematic title-card lettering with metallic bevel and rim light, embedded in the scene atmosphere, blockbuster one-sheet gravity. Small badge pill "INKED HISTORIES" in a corner away from the text. CHANNEL IDENTITY CONTRACT (non-negotiable; reject a scene that misses any visible fact): MUST SHOW: - One human historical action, artifact recovery, danger, discovery, or consequence is visibly staged at the peak moment. - Tactile cross-hatching, aged paper, charcoal depth, and a restrained ember-gold or rust proof detail anchor the illustration. MUST NOT SHOW: - AAA adventure-game key art, glossy 3D character models, RPG inventory props, comic-panel grids, speech bubbles, UI overlays, or decorative artifact-only scenes. USER-APPROVED GOLDEN CRAFT BAR: One unmistakable, story-specific hero dominates at phone size and is cropped decisively at the frame edge; a centered hero is optional only when the surrounding type and action read more strongly that way. Use only two or three meaningful visual elements with real foreground, hero, and background depth. Avoid a dead 50/50 picture-and-copy split: one hero contour or atmospheric layer should overlap the future type zone. Headline typography is oversized and treated as a physical visual object, never a generic clean font floating in empty space. Keep one forceful contrast system and one restrained accent; reject muddy, generic, or product-render imagery. Channel identity is compact and secondary in a corner; it must never compete with the hook. OWNER-SELECTED A/B PREFERENCES: Prefer a two-word concrete tension or payoff hook over a longer sentence; make the decisive noun visibly dominant. Stage the hero at the peak of action with readable emotion in face, eyes, or hands—not a calm pose after the event. Show cause and consequence together: one close hero action plus one smaller proof detail that completes the story. Bring one consequential hand, object, rupture, or release into the near foreground so the viewer feels the action before decoding the subject. Use a strong diagonal or edge crop to pull the eye from hook to hero to consequence in under one second. Allow a centered hero only for a genuinely stronger peak-action image; arrange supporting type around its silhouette and never turn it into a default symmetrical poster. Do not let a detached signboard or flat black text panel become the composition. Typography must share the scene's material, light, motion, or negative space while leaving the hero dominant. Keep headline contrast immediate and physical while preserving one clean hierarchy; do not let background lore compete.
“Let The Water Take The Weight”
Rules: 1280x720 YouTube thumbnail. The hero fills 55-75% of the frame, aggressively cropped. Typography is HUGE (owns 25-40% of the frame), ultra-bold, rendered as a designed physical object (plate, smear, strip, slab or sticker) made of the scene's material world, with one PAYOFF word 2-4x larger than the rest. HARD RULE: text NEVER covers the hero's face or eyes - beside, above or across the body only. Spelling EXACTLY as quoted - every visible word must be a correctly spelled real word. Everything must read at 120px on a phone. No play buttons, no UI, no watermarks, no extra small text. Channel "GRATITUDE SPRINGS" (signature look, obey strictly: hyperreal cinematic meditation photography, natural water and atmospheric light, tactile human or environmental serenity, restrained blue and moonlit contrast, palette #0A1A26 / #8FD3E8, accent #8FD3E8). Scene: LAYOUT MODE: centered hero at peak action; reserve asymmetric clean pockets around its silhouette for native typography. HERO PROP (dominant, 30-50% of frame, cropped close): a serene adult woman floating on her back in still dark water, face calm and turned to the sky, arms open and released, hair fanned out, the surface tension breaking into slow rings around her shoulders. BACKGROUND (separate supporting layer behind the hero - darker, simpler, depth): a moonlit spring under low mist, dark treeline dissolving into blue night, one cold band of light across the water. STORY DETAILS (symbolic, on/around the hero): concentric ripples spreading outward from her released hands; a faint breath of steam rising off the warm water into the cold air. Headline: "JUST FLOAT" (the payoff word, HUGE) (accent color) then "10 MINUTES" - placed clear of all faces. Render the headline as cinematic title-card lettering with metallic bevel and rim light, embedded in the scene atmosphere, blockbuster one-sheet gravity. Small badge pill "GRATITUDE SPRINGS" in a corner away from the text. CHANNEL IDENTITY CONTRACT (non-negotiable; reject a scene that misses any visible fact): MUST SHOW: - A serene human or environmental sanctuary visibly expresses the episode's exact emotional promise. - Water, mist, light, foliage, sky, or quiet human presence is used as a tactile calm cue where it fits the topic. MUST NOT SHOW: - A repeated rock-pile or paired-stones hero for an unrelated topic, synthetic wellness UI, plastic 3D props, or generic spa stock. USER-APPROVED GOLDEN CRAFT BAR: One unmistakable, story-specific hero dominates at phone size and is cropped decisively at the frame edge; a centered hero is optional only when the surrounding type and action read more strongly that way. Use only two or three meaningful visual elements with real foreground, hero, and background depth. Avoid a dead 50/50 picture-and-copy split: one hero contour or atmospheric layer should overlap the future type zone. Headline typography is oversized and treated as a physical visual object, never a generic clean font floating in empty space. Keep one forceful contrast system and one restrained accent; reject muddy, generic, or product-render imagery. Channel identity is compact and secondary in a corner; it must never compete with the hook. OWNER-SELECTED A/B PREFERENCES: Prefer a two-word concrete tension or payoff hook over a longer sentence; make the decisive noun visibly dominant. Stage the hero at the peak of action with readable emotion in face, eyes, or hands—not a calm pose after the event. Show cause and consequence together: one close hero action plus one smaller proof detail that completes the story. Bring one consequential hand, object, rupture, or release into the near foreground so the viewer feels the action before decoding the subject. Use a strong diagonal or edge crop to pull the eye from hook to hero to consequence in under one second. Allow a centered hero only for a genuinely stronger peak-action image; arrange supporting type around its silhouette and never turn it into a default symmetrical poster. Do not let a detached signboard or flat black text panel become the composition. Typography must share the scene's material, light, motion, or negative space while leaving the hero dominant. Keep headline contrast immediate and physical while preserving one clean hierarchy; do not let background lore compete.
buildThumbBrief emits "(the payoff word, HUGE)" as an instruction and NB2 baked the instruction into the art. That is a live prompt-injection bug in the module, not a model preference.Every posted rate found, cheapest first. The dividing line is $0.067 — Google's own batch price. Nothing below that is Google selling you an image; it is somebody reselling under wholesale.
| Provider | 1K price | Status | Reading |
|---|---|---|---|
| apimodels.app | $0.03 | claim | Sold as a “budget channel”. The same site lists a primary nanobananapro SKU separately — a cheaper channel for an identical Google model does not exist upstream. |
| reApi | $0.0322 | claim | Sells two SKUs side by side: gemini-3-pro-image-preview (default) at $0.0322 and -official at $0.124. A 3.9× gap on one platform for “the same” model is the tell. |
| Velokey | $0.0335 | claim | Flat 1K/2K. Exactly −75% of list, which is under Google’s batch wholesale. |
| APIYI / laozhang.ai / aiandapi | $0.05 | claim | Relay class. Alipay/WeChat, “direct access from China”. Below batch wholesale. |
| Google Batch API | $0.067 | floor | First-party, 50% off standard, async up to 24h. This is the real floor — the lowest price Google itself will sell a Pro image for. |
| Pixapi | $0.08 | claim | Just under batch. Plausible only on a subsidised margin. |
| kie.ai | $0.09 | credible | Cheapest synchronous reseller with a real posted rate. Caveat: kie.ai’s own /nano-banana page quotes ~24 credits ≈ $0.12 for the same model — their two pages disagree. |
| Muapi | $0.12 | credible | Posted per-image, no credit maze. |
| Modellix | $0.1265 | credible | Sits just under Google list. |
| Google direct / OpenRouter | $0.134 | credible | List price. OpenRouter verified at exactly Google’s token rates, zero markup. |
| Replicate | $0.14 | credible | ≈ list. |
| fal.ai | $0.15 | credible | What this repo pays today. Most expensive endpoint found. |
scene_forgedThe brief: typography that takes the scene's shape, lighting, angle and symmetry while staying very prominent and distinct. The key constraint, learned the hard way over two failed attempts: it must stay typography — a graphic layer that borrows the scene's light — and never become a physical prop sitting in the scene.
| Attempt | What it produced | Why it failed |
|---|---|---|
metal_monolith v1 | Colossal brushed-metal slab, support line tucked under one end | Prominent but detached — and the “never centred” rule actively destroyed the symmetry that made the original render work. |
scene_forged v1 | Brass plaque on the table; letters carved into stone blocks | Over-corrected. Perfect lighting and perspective, but the type became a prop: small, recessive, and an object physically covered the letters (“BY DA_N”). |
scene_forged v2 | Below | Stays type, owns 25–40% of the frame, borrows the key light / colour temperature / atmosphere, sits on the scene's axis, and no object may cover a letter. |
Nano Banana Pro at 2K. Channel badge pinned bottom-right on all three. Remaining gap: on Gratitude Springs the model chose an asymmetric composition, so the “centre on the scene's axis” clause never triggered — symmetry there is a composition instruction (centred hero), not a type one.
A centred hero was previously a grudging exception in two places — the art director was told to "use split by default" and "choose centered_hero only when materially stronger", and the owner rules said "allow a centered hero only for…". Both now treat it as an equal, deliberately powerful option: the subject met head-on, symmetrical, one-point perspective, with converging depth and foreground tension. The only banned outcome is a flat, evenly-lit title card with a small object floating in the middle.
Two new heist channels were added to the identity resolver to test it in deliberately different visual worlds — both rendered centred, on Nano Banana Pro at 2K, using scene_forged.
| Channel | Identity contract | Centred result |
|---|---|---|
Vault Breachvault-breach-mechanism-realism | Must show one real security mechanism being defeated by human hands and tools, met head-on with converging perspective. Bans the striped-top burglar cliché, guns, money piles, holographic security UI and 3D vault renders. | One-point corridor converging on the drill bit at dead centre, single caged worklight, dust jetting toward the lens, severed shackle in the near foreground. The symmetry is doing the work, not flattening it. |
The Getaway Filesgetaway-files-period-caper | 1970s film still: 35mm grain, halation, faded print stock, period-accurate props. Bans any modern technology, digital sharpness and gun violence as subject. | Symmetrical terminal lounge, subject dead centre holding the lens, two identical cases in the near foreground, tungsten halation. No anachronism in frame. |
Every existing gate in this module checks execution — identity contract, safe zones, copy fidelity, spelling. None of them asked whether the story was worth telling. The Vault Breach render below is technically correct on every one of them and still dull, because concrete is an inert material and a measurement of an inert material carries no human stake.
thumbnailStoryInterest.ts scores the subject deterministically before any image is paid for, and buys one text-only re-plan when the concept is weak.
| Concept | Score | What the gate said |
|---|---|---|
| “18 INCHES / OF CONCRETE” drill on a vault door | 45 weak | “the headline is a measurement of an inert material, not a stake”; “names no consequence — it describes rather than threatens” |
| “6 MONTHS / NEXT DOOR” first re-plan attempt | 60 weak | Number now measures human audacity, but still no consequence named — rejected again |
| “6 MONTHS / NOBODY NOTICED” man breaking through, cot and crossed-off calendar behind him | 85 compelling | Human at the moment of consequence + audacity marker + a named reversal |
buildThumbBrief emitted "(the payoff word, HUGE)" inside the same quoted run it told the model to spell verbatim, and BANANA_RULES carried bare HUGE and PAYOFF as emphasis. Nano Banana 2 rendered PAYOFF onto an Investory thumbnail; Nano Banana Pro rendered HUGE onto an Inked Histories one. Not a weak-model artifact.thumbnailOcrMatchesExpected only computed missing. A leaked word adds no missing copy, so the candidate scored exact and shipped. The gate was one-directional.HUGE and PAYOFF no longer appear anywhere in the prompt, in any case.leaked: any art-direction word rendered into the artwork that is not part of the planned copy. Verified against the real failing render: the NB2 “PAYOFF” image passed the old gate and fails the new one.misspelled ("GONE QUIETLI" → "GONE QUIETLY") and fail closed.Everything above froze a scene so each model received identical bytes. These two received only a channel name, a video title and the channel's own identity — no hand-written scene, hero or headline. renderCandidate() invented the layout, hero, background, story details and copy itself, so the story-interest gate, identity contract, golden craft bar and badge signature all ran for real.
| Channel | Title given | What the module decided on its own |
|---|---|---|
Sealed Recordssealed-records-document-evidence | “The Secret Deal That Buried The Epstein Case For A Decade” | Chose gloved hands lifting a heavily redacted non-prosecution agreement from a weathered evidence box under hard institutional light, and wrote “BURIED / DEAL”. The identity contract bans any recognizable real person, face or likeness, so the exposé is told through the record — no likeness was generated, structurally rather than by request. |
Overbuiltoverbuilt-structural-critique | “Why The Burj Khalifa Is A Terrible Building” | First concept scored 15/100 inert — “no human presence or agency in the hero; the headline names no consequence”. It re-planned and landed on a sanitation worker wrenching a waste hose into one of a queue of tankers, the tower hazy behind him, and wrote “NO / SEWERS”. Anchored on the documented sewage-trucking issue, not invented damage. |
Badges resolved from channel constants: Sealed Records → solid_pill, Overbuilt → engraved_plate. Both bottom-right. Leak and spelling guards clean on both.
Two constraints the module could not express at all, found by auditing where its rules were one-sided.
| Was | Why that is narrow | Now |
|---|---|---|
Energy: spectacle / bold / cozy_popmodule comment: “ALL are catchy — none are sleepy” | All three are LOUD. A disaster, a death toll, a grief or health topic, an investigation or a legal reckoning cannot be staged as “impossible scale” or “adorable” without reading as tasteless — and hype on serious material is what makes a channel look untrustworthy. | sober — arresting through restraint, not saturation. It also flips the pipeline's hardcoded “Hyper-saturated, volumetric light”, which was applied unconditionally to every channel. |
Layout: split / centered_hero | No way to express a comparison — before/after, promise vs reality, then vs now — one of the largest genres on the platform. | comparison — two heroes at comparable scale across a physical seam the scene provides (a torn edge, wall, mirror, horizon, shadow line), never a drawn divider bar, with the difference as the loudest thing in frame. |
| Story interest: one universal rule — a human carries the stake | Wrong for a whole class of video. Asked for the Burj Khalifa it scored the tower-only concept inert for having no human hero, re-planned, and produced a sanitation worker in the foreground with the show-stopper reduced to background haze. | subjectClass on the channel contract: icon leads with the structure, person with the face, event keeps the human-agency reading. |
story interest 20/100 (inert) — the icon this video is about is not the hero, re-planned, lifted to 70/100 (compelling). The tower is now the show-stopper at full height and the tanker queue is the small supporting flaw at its base — the inverse of the previous attempt.sober tier suppressing the pipeline's unconditional saturation push. No victim depicted, no tabloid red arrows.Four rounds of feedback, each one a framework gap rather than a bad roll.
| Problem | Root cause | Fix |
|---|---|---|
| The tower kept receding | The module had no camera vocabulary at all — it could not express “tilt up the tower”. Scale was the only lever it had. | New vantage axis: worm_tilt_up / high_angle / close_macro / eye_level, defaulting to worm's-eye for icon subjects. |
| “ZERO SEWERS” — the wrong criticism | Nothing ranked the available criticisms; waste collection scored the same as a death toll. | Doctrine: rank by what actually harms people or breaks the promise. An operational quirk “makes the video look small and the critic look petty”. Result: VANITY TRAP. |
| Sober was flat and static | I conflated restraint of treatment with absence of drama. | Rewritten: “RESTRAINT GOVERNS THE TREATMENT, NOT THE DRAMA.” The moment can be a rope failing or a worker swinging off an edge; what stays restrained is colour, light and taste. No gore, no body, no identifiable victim. |
| Comparison produced one confusing building with both words stacked in a corner | Two separate bugs. The copy always landed in ONE type zone so neither word labelled a half — and the art director, free to choose, never selected the comparison layout at all. | Per-side placement in the brief, plus deterministic routing: a title that states a comparison no longer leaves the layout to the model's judgement. |
The Epstein thumbnail failed repeatedly against the Gemini Developer API — finishReason=IMAGE_OTHER, a policy refusal on synthesising a real person's likeness. I concluded from that it could not be done without a licensed reference photo. That conclusion was wrong, and it was wrong because I was testing against the wrong endpoint.
This repo's production route is fal.ai, not the Gemini API. The identical module, identical channel identity and identical generated prompt render fine there. Same underlying model family, different provider policy layer.
torn_strip headline. No victim, no minor, no depiction of the act, no fabricated conduct — a portrait over documentary collage, which is what the identity contract requires and what the genre's top thumbnails actually do.Three renders were bought in this round; most of the verification was free, against files already on disk. All three answered a design question rather than decorating a claim.



A single title and the channel identity, everything else decided by the module. Rendered through the production fal route with every gate live.
| Gate | Result |
|---|---|
| Identity contract | Cross-hatched ink engraving, ember-gold torchlight, aged parchment, no anachronism — inked-histories-engraved-action held |
| Copy leak / spelling | clean — no instruction words, no misspellings |
| 120px squint | pass, contrast 131 (goldens 147–230, the weak reference 88) |
| Sameness vs the channel's own golden elephant | no flag — visual distance 28, hero overlap 0.1 |
| Story interest | 60/100 weak — “the headline names no consequence; it describes rather than threatens”. Re-planned once, scored 60 again, kept the original. |



uiClean and regenerates; it shipped here only because this harness calls renderCandidate directly and bypasses the QA verdict.The complaint was a muted orange/gold bias with weak contrast and shrinking variety. Measured across eleven renders: seven sat in the 30-62 degree amber band at 18-39% saturation. It was real.
| Run | Hue spread across the set |
|---|---|
| The approved golden references | 107 degrees (47 to 170) |
| My first run — all amber | 22 degrees |
| My second run — all blue | 19 degrees |
| Third, with a neutral palette | 25 degrees |



renderCandidate called directly, single shot, with nothing feeding failures back. In production the contrast and monotony findings land in verdict.reason, become priorIssues, and drive a regenerate. So these numbers are the WORST case — the generator was never told it had failed.Every earlier measurement was open-loop: renderCandidate called directly, single shot, nothing telling the generator it had failed. This wires the gate in so a measured contrast failure becomes priorIssues and drives a regenerate, exactly as production does. Every candidate on the shipping model — drafting disabled.




| Open loop (earlier) | Closed loop | |
|---|---|---|
| Hannibal contrast | 86, 103, 77 — three separate attempts, all failed | 93 → 110, repaired in one iteration |
| Pass rate | 1 of 3 | 3 of 3 |
| Hue spread across the set | 19–25 degrees, monotonous | 62 degrees, not monotonous |
HEADLINE_LIFT contained the sentence “the approved reference for exactly this subject reads ROME'S WORST NIGHTMARE” — I had written the answer into the module and then presented the output as reasoning. All quoted headlines are now removed from the lift and its documentation; it teaches the construction with no worked example, precisely so it cannot hand over an answer again.Studying the reference properly produced six craft rules, and several contradict what the module had been instructing. It had been asking for hard directional light, atmospheric drama, and the obvious heroic moment.
| What the reference actually does | What the module had been asking for |
|---|---|
| Sets it somewhere unexpected — not the walls of Rome, an armoured elephant in an alpine blizzard | the dramatic, obvious moment |
| The surprising object is the hero — the elephant owns the frame, the commander is small on its back | the most important person as hero |
| Near-monochrome field plus ONE saturated accent — a whole field of snow-grey, one red | hard directional light and rich atmosphere |
| Scale via a device — a countable column of tiny figures receding into a valley | “atmosphere carries the scale” |
| Type on genuinely empty ground — an entire empty half of snow | type overlapping the hero contour |



| Run | Hue spread |
|---|---|
| Amber monoculture | 22 degrees |
| Blue monoculture | 19 degrees |
| Previous closed loop | 62 degrees |
| This run | 106 degrees — the approved golden set is 107 |
Every frame in the previous round had the same defect: a flat colour panel down one side with the headline floating on it. That was caused by a rule I had written — “put the type on genuinely empty ground” — which the model read literally and solved with a rectangle. The reference's empty half is snow: real environment, edge to edge, empty but not blank.
| Seam strength | |
|---|---|
| Four approved golden references | 1.12 – 1.64 |
| My panelled frames | 2.51 – 2.53 |
| After the detector drove the loop | 1.26 – 1.93, all inside golden range |




| Defect | Root cause | Fix |
|---|---|---|
| Judge never answered | Not the route, as I had assumed and "fixed" once already. maxTokens: 400. The identical call succeeds in isolation and fails in place, because the judge prompt prepends the whole doctrine and the model spends its budget reasoning before emitting the object — the JSON was being truncated mid-string, which surfaces as a contract error and looks like a routing problem. | 2,000 tokens. Answers 3 of 3, and immediately caught the exact complaint: “GHOST LOOP” 85 → 48, “stages the passive half of the trick, omitting the active crime”. |
| Three arms passed everything | Two failures stacked. faceClear only ever asked about the FACE and uiClean about glyphs and watermarks, so no gate in the module had ever checked limbs. And my own harness used deterministic-only critique — I removed the vision reviewer to keep a contrast measurement clean, and removed the only gate that can see a body. | The QA prompt now counts limbs, hands and fingers explicitly. Verified against the failing frame: faceClear=false — "an extra hand visible holding the paper while both hands hold the head", while the golden reference still passes. |




Every thumbnail had drifted to a metal plaque. The cause was two-part and it turned out to be a whole class of bug, not one mistake: five of six channels were set to the same type motif, and that motif's own description led with “a bronze or iron plate” while the fallback motif specified “metallic bevel”. Metal was the only road.




A single constant fallback reads as a sensible safety net and silently makes every channel that omits a field identical to every other channel that omits it. That is the mechanism by which a capable module produces a monoculture. Seven were found.
| Constant | What it caused | Now |
|---|---|---|
accentColor ?? "#ffd400" | The amber bias, hard-coded. Any channel without a second palette colour got gold, permanently — the measured source of seven of eleven renders landing in the amber band. | spread across eight accents around the wheel |
textZone ?? "left" | An unset headline never moved across an entire catalogue. | spread across five zones, seeded on channel AND title |
background ?? "deep dark gradient" | Not a place — the absence of one. It produced the flat fields the seam detector later had to catch. | five real environments |
baseColor ?? "#111827" | One navy for every channel. | five dark bases |
composition: 2 values | The narrowest enum in the module — every channel was one of two construction languages. | six: adds macro_detail, silhouette_field, overhead_layout, repetition_field |
Given only what the pipeline gets — a channel name, a video title, and channel-level Style DNA. No hand-written scene, no hand-written headline, no pre-chosen motif. Two have no entry in the identity resolver at all, so they ran on pure Style-DNA foundation, and one had a deliberately short palette so the accent fell through to the new spread default.



| What happened | Why | Fix |
|---|---|---|
| “FÖÖLED” shipped misspelled with umlauts. | The vision reviewer CAUGHT it — it returned text=false on that iteration. But the critique loop exhausts its iterations and then returns the best-scoring candidate regardless, so a caught copy defect still came back. Production fails closed on this through assertThumbnailGate; the harness does not. | Reported honestly rather than quietly re-rolled. A copy defect should be a hard reject, not "best of a bad set". |
| All three thrashed, scoring 0 and 15 out of 100 and burning re-plans. | Every one is an OBJECT channel — an empty picture frame, a hull bolt, a crisp packet — and with no subjectClass declared they fell to the event reading, which penalises "no human presence or agency in the hero". They were being punished for their subject being exactly what the video is about. The same failure the icon class was created to fix, in a class nobody had named. | New object subject class: scores whether the thing is treated as EVIDENCE — held, measured, opened, cut, compared — rather than merely photographed. The failing-bolt concept moves from 15 (inert) to 60, while an object simply sitting on a shelf correctly stays inert, because a product shot is still not a thumbnail. |
The plaque was "fixed" twice before this and neither took, because both fixes were downstream of the cause. FAMILY_VISUAL_LANGUAGE assigns the motif FIRST — one fixed value per family — and narrated_stock, the most-used family, mapped to block_plate. Every channel built on it inherited a plate before any channel-level or terminal default could apply. Families now carry motif sets chosen by channel identity.



block_plate remains a legitimate member of the narrated_stock set — it is a real motif that the approved references use — and Blank Frames hashes onto it. So the honest score is three of three plaques reduced to one of three, not eliminated. Removing it entirely would trade one forced choice for another, which is the mistake this whole fix exists to undo.falNanoBananaProThumbnailContract.ts pins fal-ai/nano-banana-pro at outputImageUsd: 0.15. Same model, same output:
Sources: ai.google.dev/gemini-api/docs/pricing (first-party, read directly) · openrouter.ai/api/v1/models (live JSON) · fal.ai/pricing · kie.ai/nano-banana-pro · aggregator listings for the "reported" rows. Prices are per image at 1K unless stated. Measured figures are this run's own usageMetadata.