●DEPRECATION — The gemini-omni-flash-preview endpoint retires on September 30. Its replacement, gemini-omni-1.1-flash, reached general availability on August 27, so this one needs attention this month●MIGRATION — The swap is often a single model string, but the default resolution is now 720p. Carry over your preview-era assumptions unchanged and both your output and your bill will shift●OMNI — Version 1.1 closed four gaps: scene extension out to 40 seconds, first-and-last-frame control, a cheap 360p draft mode, and 4K upscaling●COPILOT — Gemini 3.8 Flash became selectable in GitHub Copilot on September 3, widening its reach a day after arriving in the Gemini API, AI Studio, and Antigravity●PRICE — The $0.75 input and $3.75 output per MTok on 3.8 Flash is introductory. It expires December 31, and from January 1, 2027 the rate becomes $1.50 and $7.50 per MTok●SPEECH — Gemini 3.5 Transcribe is a pair of dedicated speech-to-text models, with utterance-level language detection across 85+ languages, speaker diarization, word-level timestamps, and custom vocabulary biasing●DEPRECATION — The gemini-omni-flash-preview endpoint retires on September 30. Its replacement, gemini-omni-1.1-flash, reached general availability on August 27, so this one needs attention this month●MIGRATION — The swap is often a single model string, but the default resolution is now 720p. Carry over your preview-era assumptions unchanged and both your output and your bill will shift●OMNI — Version 1.1 closed four gaps: scene extension out to 40 seconds, first-and-last-frame control, a cheap 360p draft mode, and 4K upscaling●COPILOT — Gemini 3.8 Flash became selectable in GitHub Copilot on September 3, widening its reach a day after arriving in the Gemini API, AI Studio, and Antigravity●PRICE — The $0.75 input and $3.75 output per MTok on 3.8 Flash is introductory. It expires December 31, and from January 1, 2027 the rate becomes $1.50 and $7.50 per MTok●SPEECH — Gemini 3.5 Transcribe is a pair of dedicated speech-to-text models, with utterance-level language detection across 85+ languages, speaker diarization, word-level timestamps, and custom vocabulary biasing
Two Ways to Make a Longer Shot in Gemini Omni 1.1, and Two Different Ways They Break
Omni 1.1 extends scenes in 10-second steps up to 40 seconds cumulative, and interpolates between a pinned first and last frame. Which one you use is decided before your first generation, not after. Here is the planner I run first and the drift check I run last.
Early this month I was rebuilding a short promo clip for one of the wallpaper apps I run. The plan was simple: chain a few short cuts together and land on the app icon rising into frame.
I stopped after the third extension. Every seam was smooth. Watching it end to end, nothing looked broken. And yet the colour of the final cut had settled somewhere different from the opening cut.
The icon plate I had prepared was matched to that opening cut. The video was the thing that had moved, and fixing it meant regenerating everything I had chained on.
What I had misunderstood was treating scene extension and keyframe interpolation as two routes to the same length. They are not. They break in different places — and because they break differently, the choice belongs at the start, not at the moment you get stuck.
The 40-second ceiling cannot be moved once you are running
Scene extension in Omni 1.1 continues an existing clip. It works in 10-second increments, up to a cumulative total of 40 seconds. Those figures come from Google's own announcement of Omni 1.1 Flash.
The awkward part is what the ceiling closes off. You cannot take thirty seconds you built by extension and re-cut them into an interpolated chain afterwards, because pinning both ends requires having both end frames in hand at generation time.
So the ceiling is a deadline for a decision as much as it is a limit on length.
Extension reads the previous ten seconds
The change in Omni 1.1 is how much context the model carries forward. Earlier models referenced only the final second; this one analyses up to ten seconds of prior footage. Seams got noticeably better because of it.
That improvement is where I got caught. The better each seam becomes, the less the drift shows up at the seams.
Each join is faithful to the ten seconds before it, and comparing neighbouring cuts tells you nothing is wrong. But the fidelity is to the previous footage, not to the first footage. Step correctly enough times and you arrive somewhere far from where you started.
The call itself is unremarkable. This is the shape Google documents for extension:
from google import genaiclient = genai.Client()interaction = client.interactions.create( model="gemini-omni-1.1-flash", previous_interaction_id=previous_video_interaction.id, input=[ {"type": "text", "text": "Continue the scene."} ], response_format={ "resolution": "360p", },)
One field pointing at the previous generation. The ease of writing it and the difficulty of undoing it live in the same line, which is worth keeping in mind.
✦
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
✦You'll be able to choose between scene extension and keyframe interpolation before you spend anything on the first generation
✦You'll be able to catch the failure where every seam looks clean but the whole chain has drifted away from its opening frame, using a numeric threshold instead of your eyes
✦You'll know exactly what a 360p draft at a third of the cost can decide for you, and what it quietly cannot
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.
Interpolation pins both ends and hands you the middle
Keyframe interpolation takes a starting frame and an ending frame and generates the shot between them. Google recommends it for camera orbits, zoom transitions, and clips that need to loop cleanly.
The real difference from extension is where responsibility sits. With extension, the model decides what happens next. With interpolation, you decide where it lands and the model decides how it gets there.
The cost is that the middle is no longer yours. If the motion comes out wrong, your levers are the two end frames and the prompt — you cannot swap out a second in the middle.
If the last frame is already decided, don't extend toward it. Pin it and generate inward. A promo that ends on an icon, a loop that returns to its opening composition: both have a fixed landing, and trying to drift into that landing with extensions is the long way round.
The question that splits the two is whether the landing frame exists yet
Consideration
Scene extension
Keyframe interpolation
Length ceiling
40s cumulative, in 10s steps
No ceiling once you chain segments
Seams
Smooth, but drift from the opening accumulates
Matched at pinned frames, so nothing accumulates
Landing frame
Unknown until it is generated
Must exist beforehand
Unit of rework
Everything downstream of the mistake
The one segment that failed
Best suited to
Branching a story, exploring an ending
Logos, UI, returning to a composition
What drafts help with
Choosing the prompt
Producing the keyframes
For me the heaviest row is rework. Extension propagates failure forward. Interpolation contains it in one segment.
Work out the shot plan before you generate anything
Left to a deadline, I will always slide into "generate one and see." So the planning now happens on paper first, in a function small enough that running it costs nothing.
base_seconds is what a single generation produces in your own setup. That varies, so it is a parameter you measure and pass in rather than something I would assert for you.
import mathEXTEND_INCREMENT = 10.0 # documented: 10-second stepsEXTEND_CAP = 40.0 # documented: 40 seconds cumulativedef plan_shot(target_seconds, base_seconds, ending_frame_fixed): """Decide how to build the length before generating anything. base_seconds is your own measured single-generation length.""" if target_seconds <= base_seconds: return {"mode": "single", "generations": 1, "seams": 0, "reason": "fits in one generation"} if ending_frame_fixed: segments = math.ceil(target_seconds / base_seconds) return {"mode": "interpolate", "generations": segments, "seams": segments - 1, "pinned_seams": segments - 1, "reason": "the landing frame is fixed, so pin both ends"} if target_seconds <= EXTEND_CAP: need = target_seconds - base_seconds steps = math.ceil(need / EXTEND_INCREMENT) return {"mode": "extend", "generations": 1 + steps, "seams": steps, "pinned_seams": 0, "reason": f"fits inside the {EXTEND_CAP:.0f}s cumulative cap"} segments = math.ceil(target_seconds / EXTEND_CAP) return {"mode": "interpolate_chain", "generations": segments, "seams": segments - 1, "pinned_seams": segments - 1, "reason": f"{target_seconds:.0f}s exceeds the {EXTEND_CAP:.0f}s cap"}
The same 24 seconds flips between two build methods depending on one boolean. Both routes show two seams, but one kind accumulates and the other does not, which is why counting seams alone tells you very little.
Track seam drift with numbers rather than with your eyes
The failure I walked into was invisible in sequence and obvious side by side. That asymmetry is what a small numeric check is good for.
This one looks only at colour distribution. It says nothing about motion or composition, but it stops the specific accident I hit.
import numpy as npfrom PIL import Imagedef _hist(img, bins=32): a = np.asarray(img.convert("RGB").resize((160, 90)), dtype=np.uint8) h = np.concatenate([ np.histogram(a[:, :, c], bins=bins, range=(0, 256))[0] for c in range(3) ]).astype(np.float64) return h / h.sum()def tone_distance(img_a, img_b): """0.0 (identical) to 1.0 (unrelated). Normalised L1 over colour histograms.""" return float(np.abs(_hist(img_a) - _hist(img_b)).sum() / 2.0)def audit_chain(segment_frames, seam_limit=0.06, anchor_limit=0.15): """segment_frames: [(head_frame, tail_frame), ...] in generation order.""" anchor = segment_frames[0][0] rows, ng = [], [] for i, (head, tail) in enumerate(segment_frames): seam = tone_distance(segment_frames[i - 1][1], head) if i else 0.0 drift = tone_distance(anchor, tail) rows.append((i + 1, seam, drift)) if seam > seam_limit: ng.append(f"seg{i+1}: seam {seam:.3f} > {seam_limit}") if drift > anchor_limit: ng.append(f"seg{i+1}: drift from anchor {drift:.3f} > {anchor_limit}") return rows, ng
Before trusting thresholds on real footage, it is worth checking the checker against a chain you have deliberately tinted. Five segments joined exactly, each one shifted slightly further:
Every seam reads 0.000. The distance from the anchor climbs one step at a time and crosses the line at the fifth segment. That is precisely the thing my eyes could not hold on to.
A regenerated segment dropped into the middle trips the other row instead:
I set 0.06 and 0.15 where "you'd notice if someone pointed it out" sits in my own material. Measure two of your own frames before adopting them.
What a 360p draft can decide, and what it cannot
Omni 1.1 generates 360p previews up to 60% faster and at a third of the cost of standard 720p. That is a meaningful amount of extra iteration.
But drafts pay off in different places depending on the route. For extension, what you are drafting is prompt wording, so staying at 360p throughout is fine. For interpolation, what you are drafting is the end frame itself — and a 360p frame cannot be pinned into a 720p shot.
Mix those and a draft quietly becomes a production keyframe. As an indie developer I keep everything in one working folder, and I have made exactly that mistake, so there is now a gate before anything is handed over.
from PIL import ImageRESOLUTION_HEIGHT = {"360p": 360, "720p": 720, "1080p": 1080, "4k": 2160}def check_keyframes(first_path, last_path, target="720p"): """Vet the two frames you are about to pin.""" need_h = RESOLUTION_HEIGHT[target] problems = [] sizes = [] for label, path in (("first", first_path), ("last", last_path)): with Image.open(path) as im: w, h = im.size sizes.append((w, h)) if h < need_h: problems.append( f"{label}: height {h}px is short of {target} ({need_h}px)") (w1, h1), (w2, h2) = sizes if abs(w1 / h1 - w2 / h2) > 0.01: problems.append(f"aspect mismatch: first {w1}x{h1} vs last {w2}x{h2}") return problems
Three combinations, checked:
720p + 720p : no problems720p + 360p draft : ['last: height 360px is short of 720p (720px)']720p + square 1080 : ['aspect mismatch: first 1280x720 vs last 1080x1080']
The aspect check earns its place when you are producing vertical announcement assets and horizontal store assets in the same session. Leaving a 1080x1080 frame as the landing on a horizontal shot produces something that looks plausible enough to slip past a glance.
Where you take the final resolution up is a separate call. I wrote about receiving 4K from the API versus upscaling just before distribution in the note on where to take Omni Flash to 4K.
Three things I now clear before production
Each of these takes minutes to avoid up front and costs a whole chain if you notice it late.
Do not change resolution mid-chain. Mixing drafts and production output into one chain means you can no longer tell where production started. Once the 360p pass has settled the prompt, rebuilding from the first generation is the faster road.
Keep the interaction id next to the artefact. Extension only lets you point at a restart position by id. When I was tracking takes by filename alone, there was no way back to a take three steps earlier.
Confirm how the cumulative total counts in your own chain before you estimate. How discarded takes are treated is worth verifying once with your own runs; until then, leave slack in the plan.
The order I follow now
Write down the target length and whether the ending frame is already decided.
Run plan_shot to fix the route and the number of generations.
For interpolation, produce both keyframes at production resolution and run check_keyframes.
For extension, settle the prompt at 360p, then move up to production resolution.
After generating, run audit_chain on both seams and anchor drift.
If a threshold is crossed, do not go back and re-join — switch to interpolation from that point forward.
Step six is a note to myself. When drift appears, the tempting move is to re-join the seam more carefully, and re-joining does nothing about something that accumulates.
One deadline sits alongside all of this: gemini-omni-flash-preview shuts down on 30 September. If you are still on preview without extension available, migrating comes first. I put the deadline inventory in the note on the 30 September shutdown.
Short clips decide early
What surprised me is that the weight of the decision does not scale with the length of the video. A thirty-second announcement clip still punishes a late choice with a total rebuild.
Before your next short clip, settle one thing before generating: do you already have the final frame? If the answer is yes, the route picks itself.
I still catch myself skipping the keyframe work and reaching for another extension. Holding the order is the part I try not to give up on. Thank you for reading this far.
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.