Most teams begin with a browser tab: type a prompt, wait, download a clip, repeat. That approach survives contact with a five-shot teaser. It collapses the moment you need two hundred shots that share a cast, a color grade, a lens language, and a delivery date. The problem is rarely the model. The problem is that every decision lives in someone's head, in a chat history, or in a filename like final_v3_REAL_final.mp4.
Treating video generation as infrastructure means the pipeline itself becomes an artifact you can read, review, diff, and roll back. A shot is no longer a prompt someone typed; it is a record in a spec file. A render is no longer a favor from a colleague; it is a job with an ID, an owner, and a spend ceiling. When a client asks for the warehouse at dusk instead of noon, you change one value and re-run instead of re-prompting a hundred clips by hand.
The payoff compounds in four places:
This guide is a practical walkthrough of how to build that pipeline: what goes in the spec, how to orchestrate jobs, how to keep faces and lighting stable, and how to judge whether a result is genuinely photorealistic or merely high-resolution.
A coded video pipeline has four stages, and every stage has a clear input and output. Keeping them separate is what makes the system debuggable.
The spec is the source of truth. It describes shots, characters, locations, wardrobe, camera behavior, aspect ratios, and delivery targets. It contains no model API calls and no file paths to rendered media. Think of it as the screenplay plus the production bible, expressed in YAML or JSON.
A compiler step validates the spec and expands it into concrete render jobs. It resolves references (character: mara becomes the locked reference image set), picks a model per shot based on declared requirements, applies defaults, and produces a job manifest. Compilation should be fast, deterministic, and runnable in CI without touching a GPU.
Workers pull jobs from a queue, call the chosen generation service, poll for completion, download the asset, and write back metadata. This stage is the only one that costs real money and real time, which is why it must be idempotent.
Takes move through review states. A shot is only releasable when every required take has passed automated checks and at least one human approval. The release step assembles approved takes into an edit decision list, not a folder of videos.
If you can describe your pipeline in those four verbs — declare, compile, render, approve — you can rebuild it on a different model provider in a week.
Start small: one scene, three shots, one character. Resist the urge to model the whole production before anything renders.
project: warehouse-teaser
look:
palette: cool-amber
contrast: medium-high
grain: 35mm-fine
aspect: '2.39:1'
characters:
mara:
refs: [refs/mara-front.png, refs/mara-profile.png]
lock: [face, hair, wardrobe]
scenes:
- id: s01
location: warehouse-floor
time: dusk
shots:
- id: s01-010
brief: wide establishing, slow dolly right, dust in light shafts
duration: 5
model_class: cinematic-t2v
seed: 4471
- id: s01-020
brief: medium, mara walks left to right, practical lights behind
duration: 4
model_class: character-i2v
refs: [mara]
A shot is an intention. A take is an attempt. Never overwrite a take; append. Store the seed with every take so a good accident becomes a repeatable asset. When a director says they liked take two from yesterday, you should be able to re-render it exactly.
Declare a class of model rather than a specific product name: cinematic-t2v, character-i2v, plate-extend, upscale. Then map classes to concrete providers in a separate routing file. When a new model ships or a provider has an outage, you edit routing, not two hundred shot definitions.
Put limits in the spec where reviewers can see them: maximum takes per shot, maximum runtime per job, and per-scene spend ceiling. Guardrails written next to the creative brief get argued about early, when it is cheap.
Photorealism falls apart on continuity long before it falls apart on texture detail. A face that shifts two millimeters between cuts reads as uncanny no matter how sharp the render is.
For each recurring character, maintain a small reference set: a front, a three-quarter, a profile, and one full-body frame under the production lighting. Lock identity fields explicitly — face geometry, hair silhouette, wardrobe. When a shot needs a character, it references the lock rather than re-describing the person in prose. Prose descriptions drift; references do not.
Define lighting once per location and time of day: key direction, color temperature, practical sources, and the ambient ratio. Every shot in that location inherits the contract unless it explicitly overrides it. This is what stops a chase sequence from looking like eleven different films stitched together.
Write checks that run on every render batch:
Failing a check should mark a take as needs-review, not delete it. Automated rejection loses good material; automated flagging keeps humans in the loop on the interesting cases.
Generation is slow and flaky. Treat it like any other unreliable remote dependency.
One job should render exactly one take. Do not batch ten shots into a single job to save queue overhead; a failure then costs you ten shots and you lose the ability to retry narrowly. Compilation can create hundreds of small jobs cheaply.
Every job needs a deterministic key derived from the spec hash, model version, seed, and parameters. Before enqueueing, check whether that key already has an approved take. If it does, skip. After a crash, the same key resumes cleanly instead of producing a duplicate. A simple object store plus a database table is enough; you do not need elaborate infrastructure to get this right.
Track spend per job, per scene, and per project. Kill switches matter more than dashboards: when a project crosses its ceiling, the queue stops accepting new jobs and pages the producer. Retry policy should be bounded — three attempts with exponential backoff, then mark the shot as blocked and surface it in the daily digest rather than silently burning budget.
A queue also gives you priority control. Trailers and client-facing cuts jump ahead of internal exploration, and you can throttle overnight batches to keep costs predictable.
Generated video is cheap to produce and expensive to organize. Schema decisions made on day one save weeks later.
Never overwrite a rendered file. Write takes to paths that encode project, scene, shot, take, and a content hash. If a take needs a trim or a color fix, that is a new derivative asset with its own path. Immutability means any edit decision list from any week still resolves.
Five tables carry most productions surprisingly far:
projects — delivery targets and global settings.shots — brief, duration, scene reference, required checks.takes — asset path, seed, model version, parameters, status.reviews — reviewer, timestamp, decision, notes.costs — job reference, spend, runtime, provider.Relational storage pays off because the questions you ask are relational: which shots in scene four have no approved take, and what did that cost?
Use a small, boring state machine: queued → rendering → rendered → checked → in-review → approved | rejected. Rejections require a reason code. Reason codes are how you learn that forty percent of retries come from one badly written brief.
Reviewers disagree about photorealism because they conflate three different things: technical quality, physical plausibility, and emotional believability. Separate them.
Resolution, frame rate, codec, and bit depth are binary. Automate them. Also automate the easy artifacts: duplicate frames, flicker measured as frame-to-frame luminance variance, and audio sync if you are generating sound.
Physical plausibility is where models fail predictably: hands, reflections, contact shadows, fabric tension, and eye movement. Build a checklist per shot type. A medium close-up needs an eye check; a wide establishing shot needs a shadow-direction check. Reviewers move faster with a checklist than with a vague instruction to look for problems.
Once a month, have two reviewers score the same ten takes independently and compare. Divergence usually traces back to an ambiguous brief, not to taste. Fixing the brief improves every downstream take.
Model choice should follow shot requirements, not the other way around. Build a routing table:
| Shot requirement | Model class | Why |
|---|---|---|
| Establishing plate, no characters | cinematic-t2v | Best at atmosphere, forgiving of small artifacts |
| Recurring character in motion | character-i2v | Identity reference support beats pure text prompting |
| Crowd or background action | fast-t2v | Volume matters more than per-frame fidelity |
| Plate extension or cleanup | plate-extend | Cheaper than re-rendering the whole shot |
| Final delivery polish | upscale | Consistent sharpness across mixed sources |
On the tooling side, keep the boring parts boring: a workflow engine such as Temporal or a plain job runner for orchestration, Redis or a managed queue for dispatch, PostgreSQL for metadata, and object storage for media. For local experimentation, a node-based interface like ComfyUI is excellent for discovering what a model can do — just make sure anything that works gets promoted into the spec, or it disappears the moment the person who built it goes on holiday.
No. A folder of YAML specs, a script that enqueues jobs, and a single Postgres table will carry a small team for months. Add a queue when you have more than one worker, and add a workflow engine when you need retries, timeouts, and scheduled batches.
Three to five is usually enough: front, three-quarter, profile, and one full body in production lighting. More references help less than consistent lighting between them.
Start with a maximum of six takes for hero shots and two for background coverage. If you routinely exhaust the ceiling, the brief is underspecified, not the model.
Derive a per-shot estimate from duration and model class, multiply by the take ceiling, and compare against scene budgets during compilation. Refuse to render when the projection exceeds the ceiling.
The principles still apply, just at a smaller scale. Two files — a spec and a routing table — plus immutable take filenames will save you more time than any prompt library.
Re-render strategically: hero shots every quarter, background coverage when a cheaper model matches quality. Because your spec is stable, comparison is a diff away rather than a full production restart.
The fastest path is a thin vertical slice. Pick one scene with three shots and one recurring character. Write the spec, build the compiler, render takes into immutable paths, and run one automated continuity check against a character lock. Then review the takes with a checklist rather than a gut feeling.
Once that loop works end to end, scaling is mostly repetition: more shots in the spec, more classes in the routing table, tighter guardrails, and a review queue that keeps pace with rendering. That is the real advantage of coded video pipelines — not that they generate photorealistic frames automatically, but that they make the hundredth shot as controllable as the first.

Alexander
Vidu Q4 alternatives compared: Kling 4.0, Seedance 2.5, Wan 3.0, and Runway Gen-4 — 4K support, input modes, durations, audio, and what each costs in credits on Domer.
The top 4K AI image generators on Domer compared: Flux 3 Klein, Nano Banana 2.1, GPT Image 2.5, GPT Image 2, and Nano Banana Pro — 4K credit costs, reference limits, and speed.
Nano Banana 2.1 vs GPT Image 2.5 Flare compared: credit costs per resolution, aspect ratios, reference images, text rendering, and which tasks fit each model.