●NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaply●OMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflows●AGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactions●MEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuously●THROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and region●DEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20●NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaply●OMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflows●AGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactions●MEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuously●THROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and region●DEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20
Putting Gemini image generation to work: from prompt design to thumbnails generated from video
A practical playbook for running Gemini image generation as a repeatable workflow instead of a lucky dip. From decomposing prompts into reproducible parts to the video-to-image automation unlocked by the Nano Banana 2 GA, with working code, a pre-publish quality gate, and a design that survives preview shutdowns.
Putting Gemini image generation to work: from prompt design to thumbnails generated from video
The biggest reason image generation eats your time is that you can't reproduce a good result after the fact — you can't tell whether the one frame that came out was good or bad, or why. Generating OGP images for the four Dolice Labs blogs (Gemini Lab among them) and promo assets for the wallpaper apps I've run as an indie developer, I kept hitting exactly that: I'd land a great image and never be able to produce the same quality again.
This article is a practical note on running image generation as a recorded, reproducible workflow rather than a one-shot gamble. The first half breaks a prompt into reproducible units; the second half moves into the "generate a single image from a video" capability that became generally available in June 2026 with Nano Banana 2 (gemini-3.1-flash-image), wiring it into indie automation. For context: my contemporary art is hand-made and uses no generative AI. I use generative AI only on the "delivery" side — asset generation for the app business and the blogs. Holding that line up front makes the judgment calls below easier to read.
A prompt is a four-part design, not a dice roll
Prompts that produce good images reliably are built from structure, not feel. Keeping records, I found the parts that actually move the result collapse to four.
Subject (what to draw), scene/environment (where and when), style (photographic, watercolor, 3D, and so on), and detail (palette, lighting, angle, emotion). Filling these four deliberately turns a vague instruction like "a person sitting" into something reproducible: "a woman in her 30s seated on a sofa by a window in the evening, lit by warm interior light, professional photographic style."
The practical payoff of separating the parts is that when you land a winner, you can isolate which part did the work. Swap only the scene and regenerate, and you compare atmosphere while holding subject and style fixed. Rewrite the whole prompt every time and you lose that comparison.
Keep a "shape" for each use case
Rather than starting from scratch each time, keep a minimal shape per use case. The skeletons I reuse across blogs and apps look like this.
For a blog hero image: "[subject that symbolizes the article] doing [task/situation]. [approachable palette]. Slightly soft photographic or flat-illustration style. 16:9 landscape." For social posts, state square (1:1) or vertical (9:16) explicitly and add one note of magazine-cover polish. For presentation assets, raise the abstraction — "3D CGI symbolizing [concept], blue and purple neon, forward-looking mood" — making the concept, not a person, the lead.
Having shapes makes the automation in the second half easier, because you can templatize the brief and just slot in the article title or the video's content to produce assets.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Decompose prompts into four reproducible parts (subject, scene, style, detail) so you can reproduce a winning result from a log instead of luck
✦Implement the video-to-thumbnail automation unlocked by the Nano Banana 2 (gemini-3.1-flash-image) GA, with working Python you can run today
✦Take home a lightweight gate that rejects broken images before publishing, plus a model-ID design that won't break when preview models shut down on 6/25
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
A classic trap that lowers reproducibility is the negative instruction. Image models are poor at "do not draw X" and often draw it anyway. "A doctor smiling warmly at a patient" is more stable than "a doctor not holding a syringe." Write the desired state in the positive.
The other trap is contradiction. "A night scene but with bright daytime light" breeds broken outputs. Just keeping time of day, light source, and palette consistent visibly narrows the swing between hits and misses on regeneration.
The same prompt still varies — record to reproduce
This is where a gamble diverges from a repeatable workflow. The same prompt still produces varying results, which is exactly why recording prompt, seed, model, and timestamp together pays off when a good frame appears.
For each asset run I drop a one-line log: the brief, the model ID used, and the filename I shipped. That alone lets me reproduce, three months later, a request I make of myself like "same tone as that article's hero image." Generation without records is starting from zero every time.
Everything so far works in the app's UI or a browser-based ImageFX. From here we put the design onto the API and generate assets without human hands. A June 2026 update widened that doorway.
Generating a single image from a video — what the Nano Banana 2 (gemini-3.1-flash-image) GA changed
In June 2026, Nano Banana 2 (gemini-3.1-flash-image) and Nano Banana Pro (gemini-3-pro-image) reached GA. For indie asset generation the biggest piece is this: you can pass a video file as multimodal context and generate a thumbnail, poster, or infographic informed by its content (video-to-image is a gemini-3.1-flash-image-class use).
Previously there was a manual step of hunting for a good frame, cutting it out, and editing it. Passing the video directly lets you start from "turn the climax of this video into one vertical thumbnail." For indie developers handling stand.fm audio or short-form clips, it's a quietly meaningful shift.
The implementation is the same google-genai SDK flow as ordinary image output, with a video part added to the input. Small videos can go inline.
import osfrom google import genaifrom google.genai import typesclient = genai.Client(api_key=os.environ["GEMINI_API_KEY"])# Nano Banana 2 = gemini-3.1-flash-image (GA, 2026-06).# This family is what accepts a video as context.IMAGE_MODEL = "gemini-3.1-flash-image"def thumbnail_from_video(video_path: str, brief: str) -> bytes: with open(video_path, "rb") as f: video_bytes = f.read() response = client.models.generate_content( model=IMAGE_MODEL, contents=[ types.Part.from_bytes(data=video_bytes, mime_type="video/mp4"), brief, ], config=types.GenerateContentConfig( response_modalities=["TEXT", "IMAGE"], ), ) for part in response.candidates[0].content.parts: if part.inline_data is not None: return part.inline_data.data raise RuntimeError("No image part returned (text-only response)")
The brief is just the "shape" from the first half. For a vertical thumbnail: "Turn the climax of this video into one image. Subject large and centered, vertical 9:16, high-contrast for legibility, no text" — the four parts plus aspect ratio. I want to place the title text reliably downstream, so telling the image model not to add text keeps the pipeline stable.
Videos beyond a few tens of MB are safer through the Files API than inline. Swap the inline bytes for an uploaded file reference in contents; the call's skeleton is unchanged.
def thumbnail_from_large_video(video_path: str, brief: str) -> bytes: uploaded = client.files.upload(file=video_path) response = client.models.generate_content( model=IMAGE_MODEL, contents=[uploaded, brief], config=types.GenerateContentConfig( response_modalities=["TEXT", "IMAGE"], ), ) for part in response.candidates[0].content.parts: if part.inline_data is not None: return part.inline_data.data raise RuntimeError("No image returned")
Even with IMAGE in response_modalities, responses come back in three mixes: text only, image only, or both. Looping over parts and treating something as an image only when inline_data is present is mandatory in automation. Skip that guard and a text-only response throws.
A lightweight gate that rejects broken images before publishing
The scary part of automation is a broken frame flowing straight to publication. I did exactly this in wallpaper color-variant generation and shipped a flat, single-color image. Before adding heavy quality scoring, stopping obvious accidents with a cheap mechanical check is the realistic move.
The gate below decides, with no extra API call, whether the aspect ratio drifted from target and whether the image is nearly single-color (a generation failure).
from PIL import Imagefrom io import BytesIOdef passes_quality_gate(img_bytes: bytes, target_ratio: float = 16 / 9, tol: float = 0.08) -> bool: img = Image.open(BytesIO(img_bytes)).convert("RGB") w, h = img.size ratio = w / h if abs(ratio - target_ratio) / target_ratio > tol: return False # aspect ratio drifted too far from target # Detect near-single-color (flat / broken): inspect pixel variance after shrinking small = img.resize((32, 18)) px = list(small.getdata()) n = len(px) mean = [sum(c[i] for c in px) / n for i in range(3)] var = sum(sum((c[i] - mean[i]) ** 2 for i in range(3)) for c in px) / n return var > 150 # empirical threshold; below it, flat/broken images dominate
The 150 threshold is just my local empirical value. It needs tuning to your material, but even using it only as a "regenerate an obviously bad frame once" retry condition noticeably dropped my manual rework rate at volume. Rather than aiming for perfect quality judgment from day one, putting a cheap gate that stops only accidents first is the better cost-benefit trade for indie work.
Don't get caught by preview shutdowns — keep model IDs in one place
One more thing that always pays off in automation: deprecation handling. Preview models shut down on notice. In fact gemini-3.1-flash-image-preview and gemini-3-pro-image-preview are scheduled to stop on 6/25, and any code that hard-coded them will fail en masse with INVALID_ARGUMENT the moment they go.
The defense is simple: don't scatter model IDs through the code — confine them to one dictionary. When something shuts down, you follow with a one-line change instead of grepping the whole codebase.
# Keep model IDs in one place so a preview shutdown is a one-line follow-up.IMAGE_MODELS = { "fast": "gemini-3.1-flash-image", # Nano Banana 2 (GA) "quality": "gemini-3-pro-image", # Nano Banana Pro (GA)}def resolve_image_model(tier: str = "fast") -> str: try: return IMAGE_MODELS[tier] except KeyError as e: # Don't silently fall back an unknown tier to a default; fail loudly raise ValueError(f"Unknown image model tier: {tier}") from e
I reuse the same generation base across several Lab sites and the wallpaper apps, so I register preview shutdown dates on an operations calendar and finish moving to GA versions the week before. Making time-bound deprecations something you "can't miss" is the best insurance when one person runs several projects.
Your next step
Start by taking one prompt that produced a winner and decomposing it into the four parts — subject, scene, style, detail — as a template. Feed that template as the brief to thumbnail_from_video() and you can run the minimal loop for generating assets from a video today. With records and shapes in hand, image generation turns from a gamble into a process you can reproduce. Thank you for reading.
Share
Thank You for Reading
Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.