Vibe Engines
YouTube

Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path

AI System Design

Design Text-to-Video

Step 1 / 9

Learn AI system design by building a Sora-style text-to-video service step by step.

The numbers to beat~100×compression into latentsjointdenoising across all framesminutesof GPU per clip

The whole design, in writing

Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.

The big idea

Why video is a different beast than images

Image generation is a two-second, single-GPU affair. Video multiplies every hard thing: a 10-second clip is hundreds of frames that must agree with each other — the corgi can't change breed between frames — and a single render holds a top-end GPU for minutes. That combination (temporal consistency × brutal compute) reshapes the entire system around one truth: you are building a render farm with a safety department, not a chat endpoint.

Userprompt in, video out
New in this step: User.

An async job pipeline: screen the prompt while it's cheap, enrich it with an LLM, schedule it onto a GPU fleet with real queue economics, generate in compressed spacetime latents, decode, moderate the actual pixels, stamp provenance, and deliver drafts before finals.

What the new pieces do

Userclient
Types "a corgi surfing a wave at golden hour, cinematic" and expects something watchable — eventually, but not silently.

Step 1 · The skeleton

Async or nothing

A render takes 2–10 minutes. An HTTP request that hangs for 10 minutes is a timeout, a retry storm, and a user staring at a spinner wondering if anything is happening. What does the front door look like?

Userprompt in, video outAPI / Job Queueasync · status
New in this step: API / Job Queue.

The user submits a prompt. What does the API do?

  1. Minutes-long connections die at every proxy, retry into duplicate renders, and scale catastrophically. Synchronous is simply unavailable at this latency.

  2. The async job pattern: submit → job ID → status stream (queued, position, rendering 40%, done). The user always knows where things stand; the system batches and schedules freely behind it.

  3. Async, yes — but the product needs live progress, previews and cancellation, not a surprise attachment twenty minutes later.

The API/Job Queue accepts the prompt, persists a job, and returns an ID in milliseconds. Status flows back continuously — queue position, progress, preview-ready, done — over polling or a push channel. Cancellation is first-class: users abandon; billing GPU-minutes for unwatched renders is how you lose them twice.

Why this piece earns its place

The hard part of this skeleton is not the queue, it is deciding who owns a job's liveness. A worker has to claim a job and keep claiming it with a heartbeat, because a GPU process that dies at minute seven otherwise leaves a row sitting in rendering forever — no error, no alert, and the first person to notice is the user writing in. Make submission idempotent on a client-supplied key, and be specific about the two details that decide whether that actually works: the key is scoped per user, and the server remembers it for longer than a render takes, or a retry arriving at minute nine starts a second job. Cancellation has to be a flag the worker reads between denoising steps; honoured only at completion it frees no silicon and refunds nothing. This row is also what makes the scheduler's preemption survivable at all — a job can be evicted and requeued precisely because its state lives in a database rather than in a worker's memory. And the failure vocabulary deserves more thought than it gets. Failed is a category, not a state: refused at screening, out of capacity, decoder crash and user-cancelled are four different sentences to the user, they bill differently, and only some of them are worth retrying without asking first.

What the new pieces do

API / Job Queuebackend
Accepts the request, returns a job ID immediately, and streams status updates. Nothing about video generation is synchronous.

Step 2 · Screen before you spend

Prompt safety, at the cheap end

Some prompts must never render: abuse material, real-person deepfakes, targeted harassment. And some merely shouldn't: trademark characters, watermark-evasion requests. When do you check — before burning five GPU-minutes, or after?

API / Job Queueasync · statusPrompt Safetypolicy · identity
New in this step: Prompt Safety.

Where does content policy enforcement start?

  1. Milliseconds of screening before minutes of GPU. Block the clear violations, flag the ambiguous for stricter output review, and refuse identity-deepfake requests outright.

  2. Output moderation is essential (step 6) but catching a violating render AFTER five GPU-minutes is the most expensive possible “no.” Screen twice; spend once.

  3. Generative models don’t refuse; they generate. Policy lives in systems around the model, never in hopes about the model.

Prompt Safety runs before queueing: fast classifiers plus an LLM policy pass for nuance, identity-protection checks (public figures, uploaded faces), and IP filters. Clear violations bounce with a reason; borderline prompts proceed tagged for stricter output moderation downstream.

Why this piece earns its place

Screening sits in front of the job ID, so its latency is the submit latency — and an LLM policy pass is not a millisecond operation. Fire the classifiers and the policy call concurrently rather than in series, and let a confident classifier verdict cancel the LLM call, so the common prompt pays for the cheap check and only the ambiguous middle pays for both. That leaves one genuinely hard decision: what the gate does when the policy model is timing out. A check this cheap next to a render tempts you to fail closed, but refusing everything through a dependency blip turns it into a full outage — and you have a second gate. Fail open, mark the job unscreened, and let output moderation run at its tightest thresholds; you pay GPU-minutes you would rather not, and nothing ships unchecked. Then alarm on that rate, because unscreened is a state you want to see rather than a default you drift into. Refusals, meanwhile, are the one output with no feedback loop — a wrongly refused user does not retry, so refusal rate cannot tell a moved threshold from a real abuse wave. Sample them into human review continuously, not after a complaint.

What the new pieces do

Prompt Safetyservice
Screens prompts before any GPU spends: violence, CSAM, real-person deepfakes, IP-protected characters — cheap checks before expensive renders.

Step 3 · Say it like the model heard it

LLM prompt expansion

Users type "corgi surfing." The model was trained on dense, descriptive captions — shot types, lighting, motion, lens language. That gap between how users write and how the model listens is worth more quality than most architecture changes. Who bridges it?

API / Job Queueasync · statusPrompt ExpanderLLM enriches
New in this step: Prompt Expander.

What happens to the user’s terse prompt?

  1. Terse prompts under-specify; the model fills the gaps with mush. "Corgi surfing" deserves camera, light, water physics — details users didn’t know they could ask for.

  2. Rewrite toward the training distribution: shot type, lighting, motion verbs, style. Show users the expansion and let power users opt out — enrichment, not override.

  3. A form with 14 cinematography fields is a product nobody uses. The LLM does the film-school translation invisibly.

The Prompt Expander — an LLM — rewrites the user’s intent into the register the video model was trained on: subject, action, camera, lighting, mood, style. The expansion is shown (and editable), preserving user control while lifting default quality dramatically. This is the same trick DALL·E 3 made famous, and it matters more for video, where motion must be described.

Why this piece earns its place

The expander is the one component that writes text nothing has screened. Safety read what the user typed; the renderer consumes what the expander produced, and a model told to add cinematography will happily supply a named actor, a recognisable logo or a style nobody approved — and a user who wants exactly that can shape the prompt so the expander says it on their behalf. So treat the expansion as an input to policy rather than an output of it, and re-run the cheap classifiers over the expanded caption before it queues. The second thing is that this component has a maintenance schedule nobody puts on a calendar. Its whole job is to sit inside the renderer's training distribution, so every time the renderer is retrained the template drifts out of tune — no error, no alert, just a slow sag in default quality that surfaces months later as vaguer videos and gets blamed on the model. Pin the expander version to the renderer version and re-tune them as a pair. At least that is the easy direction to move: a template edit ships on the next request, while the renderer is a training run.

What the new pieces do

Prompt Expanderservice
An LLM rewrites the terse user prompt into the detailed, cinematography-aware description the video model was trained to expect.

Step 4 · The economics layer

Scheduling minutes-long GPU jobs

Every render monopolizes an expensive GPU for minutes. Demand is spiky, supply is fixed-ish, and free users, pro users and enterprise API calls all want the same silicon. This isn’t load balancing — it’s an economy. Who gets the GPUs, when?

Prompt ExpanderLLM enrichesGPU Schedulerqueues · tiers · preempt
New in this step: GPU Scheduler.

How are render jobs assigned to the GPU fleet?

  1. One free user’s 4K batch starves paying customers. FCFS is fair the way a single supermarket line with no express lane is fair — badly.

  2. Video GPUs are scarce, expensive, and slow to provision — capacity moves in weeks, demand moves in minutes. Autoscaling is a supplement, never the answer.

  3. Paid tiers get priority lanes (that’s what they bought), fair-share stops any one user monopolizing a tier, background/batch jobs are preemptible filler, and the queue tells the truth about waits.

The GPU Scheduler runs the economy: priority tiers by plan, fair-share within tiers, preemptible batch soaking idle capacity, admission control that stops accepting jobs it can’t honestly promise, and wait estimates surfaced to the user. Fleet utilization and queue truth are both product features.

Why this piece earns its place

Underneath the tier logic sits a packing problem nobody warns you about. Jobs differ in length and resolution, so a fleet running one undifferentiated queue fragments: a handful of long renders pin whole nodes and a burst of short jobs has nowhere to land even while utilization looks healthy. Duration has to be a scheduling input, which means estimating it before the job runs — from resolution, requested length and model version, fitted on what jobs of that shape actually took. Splitting the fleet into pools by expected duration costs a little utilization and buys back predictability. Preemption has a price the tier table does not show: evicting a render at minute eight throws away eight minutes of silicon, so preempt early in the denoising schedule or checkpoint the latents, and track wasted GPU-minutes as a metric rather than assuming eviction is free. Publish wait estimates from a conservative percentile rather than a mean, too — an estimate that is right on average is wrong for half the people reading it, and they are the half who write in.

What the new pieces do

GPU Schedulerservice
Allocates minutes-long GPU jobs: priority tiers, fair-share, preemptible batch, and honest wait estimates. The economics live here.

Back of the envelope

priority tiers
enterprise > pro > free — the lanes people pay for
fair-share
no single user monopolizes a tier
preemptible batch
background jobs fill idle GPU troughs
honest waits
shown queue position beats silent hanging

Step 5 · The generator

Diffusion transformers over spacetime latents

Now the actual magic. Generating raw pixels for hundreds of frames is computationally insane, and generating frames independently gives you a flip-book of hallucinations — the surfboard changes color, the wave forgets physics. How do modern systems generate coherent motion at all?

Video DiTspacetime latentsLatent Decoderlatents → frames
New in this step: Video DiT, Latent Decoder.

How does the model generate a coherent 10-second clip?

  1. No shared state between frames = no object permanence. The corgi is a different corgi in frame 40. Temporal coherence must be IN the generation, not glued on.

  2. A VAE squeezes video ~100× into latents spanning space AND time; a DiT denoises all patches together, so attention runs across frames — objects persist because generation is joint. Decode returns pixels at the end.

  3. Interpolation yields smooth morphing, not action — nothing new can happen between keyframes. Fine for slideshows; fatal for surfing corgis.

The Video DiT works in a compressed spacetime latent space: a video VAE encodes clips into patches spanning height × width × time, a diffusion transformer denoises all of them jointly — attention flowing across frames — and the Latent Decoder turns finished latents back into pixels. Temporal consistency isn’t a post-process; it’s the architecture.

Why this piece earns its place

Two operational facts about this pair. Memory peaks at the decoder, not the transformer: latents are small and frames are not, so a clip that denoises comfortably can still exhaust VRAM in the decode, which reads to everyone as a random failure late in the job. Decode in frame chunks, and size the machine for the decode rather than the denoise. Second, attention cost grows with the square of the token count, and tokens are frames times resolution — so clip length and resolution are not independent knobs, and the scheduler cannot price a job from either one alone. The VAE is the piece to be careful with. Whatever the decoder cannot represent is baked into every output no matter how good the transformer becomes, and replacing it invalidates a transformer trained against its latent space: a decoder change is a retrain, not a deploy. Which is why the weights version and the seed both belong on the job and beside the stored asset. Renders minutes apart can straddle a rollout and come off different builds, and with neither field recorded you cannot answer the first question anyone asks after a bad deploy — which videos came out of which build — nor re-run a specific render later, since the weights alone will not reproduce it without the seed.

  • ~100×compression into latents
  • jointdenoising across all frames
  • minutesof GPU per clip

What the new pieces do

Video DiTgpu
The generator: a diffusion transformer denoising patches of compressed space-time, so objects persist and motion stays coherent across seconds.
Latent Decoderservice
Decompresses the finished latents into actual pixels — frames, then an encoded video file.

Step 6 · Check the pixels, sign the work

Output moderation and provenance

The prompt passed screening — but diffusion is stochastic: an innocent prompt can render policy-violating pixels, and a crafted one can smuggle them past text filters. And once released, AI video feeds a misinformation machine unless it's identifiable. Two duties before delivery.

Latent Decoderlatents → framesOutput Moderationscan frames · watermark
New in this step: Output Moderation.

The render finished. What stands between it and the user?

  1. Prompts lie; pixels don’t. Output is the only place policy can actually be verified, and borderline-tagged jobs especially need it.

  2. Vision classifiers sample frames (tighter thresholds for tagged jobs); passing videos get signed provenance (C2PA) and a robust invisible watermark so "is this AI?" stays answerable downstream.

  3. Humans can’t watch a fleet’s output at scale. Automated scans everywhere; humans on the flagged sliver.

Output Moderation scans sampled frames (and audio, if any) with vision classifiers — thresholds tightened for jobs tagged at prompt screening. Clean renders are stamped with C2PA provenance metadata and an invisible watermark that survives re-encoding, so platforms and researchers can identify AI-generated video even after the metadata is stripped.

Why this piece earns its place

Frame sampling is a real decision with a measurable miss rate: a violation can occupy a fraction of a second of a ten-second clip, so a fixed stride steps over it some percentage of the time. Sample denser around cuts and motion spikes, denser again for tagged jobs, and treat the stride as a tuned parameter rather than a constant somebody picked once. Run the same scan on the draft, not only the final — a violating direction caught at draft quality costs seconds of GPU, while the same catch after the full pass costs the whole render and the user waited for a video they were never going to receive. C2PA is a signature, which means you now operate a signing key: rotation, expiry, and a plan for the day it leaks and anything on the internet can claim to be your output. And plan for the re-scan, because policy moves and someone will eventually ask what the archive looks like under the new thresholds. Re-scanning video means decoding all of it again — a render-sized workload competing with live traffic for the same silicon, which is an argument for running it in the preemptible batch lane the scheduler already has, rather than as a script somebody kicks off on a Friday.

What the new pieces do

Output Moderationservice
Scans the generated frames (prompts lie; pixels don’t), then embeds C2PA provenance metadata and an invisible watermark.

Step 7 · Deliver drafts, then finals

Progressive rendering and the CDN

Ten minutes is a long time to wait for a video that might be wrong — wrong vibe, wrong motion, wrong corgi. And finished videos need to play instantly, worldwide. The last mile is UX economics: how do users iterate without burning full renders?

Prompt SafetyOutput ModerationAsset Store / CDNTelemetry
New in this step: Asset Store / CDN, Telemetry. · swipe to pan the diagram

How do users iterate toward the video they wanted?

  1. Five iterations × 10 GPU-minutes = an hour of silicon for one usable clip. Exploration at final quality bankrupts the fleet and the user’s patience.

  2. The film-industry pattern: dailies before final cut. A 20-second draft answers "is this the right video?" for 5% of the cost; the winner gets the full render.

  3. Text can’t answer motion questions. Only frames — cheap ones — can.

Draft-then-final: low-resolution, fewer-step preview renders come back in tens of seconds; the user picks a take and only that one gets the full-quality pass. Everything lands in the Asset Store/CDN — previews, finals, thumbnails — served from the edge, with Telemetry (queue depth, GPU-minutes/job, pick-rates) feeding back into scheduler tuning and model evals.

Why this piece earns its place

The storage bill is the part that sneaks up. Drafts outnumber finals by however many takes a user needs, and they are video, so with no retention policy storage grows with usage multiplied by iteration count instead of with usage. Expire drafts on a short clock, keep finals, and make that clock a product decision rather than a default. Delivery has an unusual shape too. A pull-through CDN only starts paying at the second request to a given edge, and a render watched once by the person who made it never gets one: a miss, an origin fetch, an extra hop, then it ages out having served nobody twice. So the number to watch here is origin egress, not hit ratio — the ratio will look dreadful, and that is simply what freshly generated private content does. Save the real caching work — tiered caching, an origin shield, long TTLs — for the assets that get shared, which is a decision you can make at the moment of sharing rather than at write time. Everything stays behind expiring signed URLs regardless, since this content is private by default. And pick-rate is biased in a way worth saying out loud: users pick the best of the drafts they were shown, never the best possible render. It tells you which option won. It cannot tell you the options were good.

  • draftsseconds, ~5% of cost
  • finalsonly for picked takes
  • edge CDNinstant playback worldwide

What the new pieces do

Asset Store / CDNstore
Durable storage plus edge delivery: draft previews, final renders, thumbnails — served fast, everywhere.
Telemetrybus
Queue depth, GPU-minutes per job, failure rates, and quality/preference signals that tune the scheduler and the models.

The payoff

You built a text-to-video system

From a one-line prompt to a watchable, signed, delivered video: an async job spine, safety at both ends, LLM prompt expansion, a GPU scheduler running real queue economics, diffusion transformers over spacetime latents, provenance watermarking, and draft-then-final rendering through an edge CDN.

UserAPI / Job QueuePrompt SafetyPrompt ExpanderGPU SchedulerVideo DiTLatent DecoderOutput ModerationAsset Store / CDNTelemetry
The finished design, end to end. · swipe to pan the diagram

Now trigger the viral surge and watch the economy under stress: honest queues, priority lanes, preempted batch, draft-first degradation — the difference between a rough day and a dead product.

Everything you assembled, in order

  • Async jobs — job ID in ms, status stream, first-class cancel
  • Safety × 2 — cheap prompt screen, truthful pixel scan
  • Prompt expansion — LLM translates users into the training distribution
  • GPU scheduler — tiers, fair-share, preemptible batch, honest waits
  • Spacetime latents — compress ~100×, denoise jointly, decode last
  • Provenance — C2PA + invisible watermark on every render
  • Draft → final — cheap iteration, expensive only for the winner
  • Telemetry — GPU-minutes and pick-rates tune the whole economy

Where an interviewer pokes next

Getting the boxes right is the easy half. These are the questions that separate a candidate who drew the diagram from one who has run the thing. Answer each one out loud before you open it.

  1. Fair-share within a tier — how does the scheduler define "fair" when one user submits a 5-second clip and another submits a 60-second one?

    Fair-share is usually measured in GPU-minutes consumed (or a similar cost proxy), not job count — a user who submits one 60-second render has used the same resource budget as someone who submitted twelve 5-second ones, so the scheduler tracks consumption against a rolling allowance per user within their tier, not "one job at a time" turn-taking. Counting jobs instead of GPU-time would let one long-render user starve everyone else while technically only having "one job running."

  2. A draft used fewer diffusion steps and lower resolution. Does the final render start from scratch, or does it reuse anything from the draft?

    Typically from scratch at full settings — the draft’s low-step, low-res latents aren’t a valid starting point for a high-step, high-res denoise, because the intermediate noise schedule and resolution don’t line up between the two configurations. Some pipelines DO reuse the draft’s seed to keep the same general composition/motion between draft and final (so what the user approved is recognizably what they get), but the actual diffusion compute is a fresh full pass, not a continuation.

  3. How does an invisible watermark survive things like re-compression, cropping, or someone screen-recording the video?

    Robust watermarking embeds the signal redundantly across many spatial and temporal locations (not one fragile spot), and in frequency-domain characteristics that survive lossy re-encoding better than pixel-exact patterns — the tradeoff is that VERY aggressive transformations (heavy cropping that removes most of the frame, or extreme re-compression) can still degrade or destroy the signal. Watermarking is explicitly a "raise the cost of removal" defense, not an unbreakable one, which is why it pairs with C2PA metadata rather than replacing it.

  4. What if the LLM prompt expander changes the user’s intent — adds a detail, a style, or an element they didn’t ask for?

    This is exactly why the expansion is shown and editable rather than applied silently — the user sees "corgi surfing" became "a corgi surfing a turquoise wave at golden hour, slow-motion, cinematic lighting" BEFORE the render starts, and can edit or reject the expansion. Enrichment without visibility would be the system quietly overriding user intent; the product answer is transparency plus an opt-out, not blind trust in the expander.

  5. How would a 60-second multi-scene video work, given the DiT does joint denoising across all frames of ONE generation window?

    One joint-denoising pass has practical limits on how many frames it can coherently attend over at once, so a multi-scene video is generated as multiple shorter clips (each internally coherent via joint denoising) and stitched — with the harder problem being CROSS-clip consistency (does the corgi look the same in scene 2 as scene 1?), which typically needs extra conditioning (reference frames, character/style embeddings carried between generations) rather than being solved by the core architecture alone.

Check yourself — the answers, and why

Eight steps in, these are the calls you should be able to make cold. Pick one, then read why.

  1. Why must text-to-video APIs be asynchronous?

    Work that outlives a request becomes a job system: persistent state, progress events, honest queues.

  2. What makes generated video temporally coherent?

    Coherence is architectural: the DiT denoises all frames together in compressed latent space, so objects persist by construction.

  3. Why moderate outputs when prompts were already screened?

    Generation is stochastic and text filters are foolable. The frames are the only place policy is verifiable.

  4. Draft-then-final rendering exists because…

    Most renders are exploration. Answering "is this the right video?" at 5% cost is the system’s biggest UX and margin win at once.

  5. A prompt passed safety screening. Is scanning the rendered pixels still necessary?

    Prompt and pixel checks catch different things: text-time screening can’t predict what a stochastic model will actually draw. Skipping output moderation ships unscanned, unwatermarked video.

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

What it must do

Agree on these before drawing a single box.

  • Generate video: take a text prompt and render a temporally-coherent clip as an async job.
  • Screen the prompt: block policy violations and identity deepfakes before any GPU spends.
  • Expand the prompt: an LLM rewrites terse intent into the cinematography-aware caption the model expects.
  • Schedule GPUs: priority tiers, fair-share, preemptible batch, and honest wait estimates.
  • Moderate + sign: scan the rendered frames, then stamp C2PA provenance and an invisible watermark.

The qualities that shape everything

Each one names the mechanism that buys it.

Survive minutes-long renders
The API/Job Queue returns a job ID in milliseconds and streams status (queue position, progress, preview-ready, done); cancellation is first-class since users abandon.
Never burn GPU-minutes on a violation
Prompt Safety runs before queueing — fast classifiers + an LLM policy pass, identity/IP checks — so clear violations bounce cheaply and borderline prompts proceed tagged for stricter output review.
Lift default quality for free
The Prompt Expander (an LLM) rewrites terse intent into the model’s training register — subject, camera, lighting, motion — shown and editable so it enriches without overriding.
Ration scarce GPUs fairly
The GPU Scheduler runs an economy — priority tiers, fair-share within tiers, preemptible batch soaking idle capacity, admission control, and honest wait estimates.
Temporal coherence, not a flip-book
A Video DiT denoises spacetime latent patches jointly so attention flows across frames — objects persist by construction — then the decoder returns pixels.
AI video stays identifiable + policy-checked
Output Moderation scans sampled frames (tighter thresholds for tagged jobs), then stamps C2PA provenance and an invisible watermark that survives re-encoding.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

Return a job ID + status stream over holding the HTTP connection until the video is ready

Minutes-long connections die at every proxy, retry into duplicate renders, and scale catastrophically. Submit → job ID → progress events is the only shape that survives a 10-minute render.

Screen the prompt before queueing over moderating only the output

Catching a violating render after five GPU-minutes is the most expensive possible “no.” Milliseconds of prompt screening block clear violations up front; output scanning still runs, but you screen twice and spend once.

Priority tiers + fair-share + preemptible batch over first-come, first-served

FCFS lets one free user’s 4K batch starve paying customers. Paid tiers buy priority lanes, fair-share stops any user monopolizing a tier, and batch fills idle troughs — the scheduler is the business model, not a load balancer.

Joint denoising over spacetime latents over generating each frame with an image model then stitching

No shared state between frames means no object permanence — the corgi is a different corgi by frame 40. A DiT denoises all patches together so attention runs across frames; coherence is architectural, not glued on.

Cheap drafts first, full quality only on the pick over a full-quality render on every attempt

Five iterations × 10 GPU-minutes is an hour of silicon for one usable clip. A fast low-res draft answers “is this the right video?” for ~5% of the cost; only the winning take gets the full render.

The answer, out loud

What a strong answer to “Design a Text-to-Video System” sounds like, first question to last trade-off. It is about 7 minutes of talking; the whiteboard and the interviewer fill the rest of the 45. Read it aloud once, then close the page and give it yourself.

  1. 0–3 min

    Name what makes video different

    Before I draw a box I want to say why this isn’t image generation with a bigger number on it. A ten-second clip is hundreds of frames that have to agree with each other, and one render holds a top-end GPU for minutes — call it two to ten. Temporal consistency times brutal compute means I’m not designing a chat endpoint, I’m designing a render farm with a safety department attached. That reframing drives every decision after it: the API has to be asynchronous, the policy checks belong where they’re cheap, and the scheduler ends up being a pricing system rather than a load balancer.

  2. 3–8 min

    The front door returns a job ID

    So, the front door. The user posts a prompt, the API persists a job and returns an ID in milliseconds, then streams status back — queued, position, rendering, preview ready, done. I wouldn’t even consider holding the connection open: minutes-long HTTP dies at every proxy, and each retry books another GPU. I’d make cancellation first class as well, because people abandon, and billing someone for a render they walked away from loses them twice. I’d commit to this shape on day one rather than grow into it, because the job row becomes what everything downstream reads — scheduler, moderation, billing — and giving an identity to work that’s already running is much harder than writing one down at the start.

    Built in step 1: Async or nothing
  3. 8–13 min

    Screen the prompt before the GPU spends

    Next I’d put prompt safety in front of the queue: fast classifiers plus an LLM policy pass, identity checks for public figures and uploaded faces, IP filters. It sits here rather than only at the end because of the cost asymmetry — screening text takes milliseconds, and the identical refusal issued once the render has run costs us the render. Clear violations bounce with a reason. The interesting case is the borderline prompt: I’d let it through but tag the job, and that tag tightens the thresholds when we scan the output later. So it’s two checks against one policy, with the first one calibrating the second.

    Built in step 2: Prompt safety, at the cheap end
  4. 13–18 min

    Translate the user into the model’s register

    Then a prompt expander, which is just an LLM. Users type corgi surfing; the model was trained on dense captions with shot type, lighting, motion, lens language. Closing that gap is the highest-leverage change here that isn’t a training run, and it matters more than it does for images, because motion has to be described and users never describe it. I’d show the expansion and let people edit it or opt out, because silently rewriting what somebody asked for is the system overriding intent rather than helping. And I’d store the expanded caption on the job, not just the original, so a re-render gives back the same video instead of a new one.

    Built in step 3: LLM prompt expansion
  5. 18–25 min

    The scheduler is the business model

    Now the part I’d spend the most time on. Every render holds expensive silicon for minutes, demand is spiky, and supply moves in weeks. First-come-first-served lets one free user’s batch starve paying customers, and autoscaling can’t rescue us because the GPUs aren’t sitting idle waiting to be scaled into. So: priority tiers by plan, fair-share inside each tier so no single user monopolizes a lane, preemptible batch work soaking the idle troughs, admission control that stops accepting jobs we can’t honestly promise, and wait estimates shown to the user. That isn’t load balancing. Utilization is the gross margin, and the queue is part of the product.

    Built in step 4: Scheduling minutes-long GPU jobs
  6. 25–32 min

    How the pixels actually get made

    For generation: a video VAE compresses clips roughly a hundred times into latent patches spanning height, width and time, and a diffusion transformer denoises all of those patches jointly, so attention runs across frames. A decoder turns the finished latents back into pixels at the end. I’d say out loud why not the alternatives: stitching frames from an image model leaves nothing shared between them, and interpolating between keyframes buys smooth morphing where the brief asked for action. Coherence has to be architectural — you get it by denoising space and time together, not by smoothing afterwards.

    Built in step 5: Diffusion transformers over spacetime latents
  7. 32–38 min

    Check the pixels, then sign the file

    Before anything ships I’d scan the frames themselves. The text gate can only judge what somebody asked for, and only the frames can tell us what came out. That leads somewhere worth saying out loud: when a clean prompt renders something we won’t deliver, the user did nothing wrong, so the right answer is a re-roll, not a refusal — refusing there accuses somebody of the model’s mistake. Then provenance, strictly in that order: scan first, sign second, because a signature on a file we never looked at puts our name on it. And I’d be careful how I sell the watermark — it’s infrastructure for everyone downstream of us, not a control over what people do with the file. I’d fund it anyway, because nobody further down the chain was there when the video was made.

    Built in step 6: Output moderation and provenance
  8. 38–45 min

    Drafts first, and where I’d go next

    Last mile, and it’s where the economics close. Nobody should pay a full render to discover the vibe was wrong, so: low-resolution, few-step drafts back in tens of seconds at around five percent of the cost, and the full pass only on the take the user picks. Exploration is most of the traffic, so cheap exploration is what makes the fleet pay for itself. Drafts and finals land in an asset store behind a CDN, and telemetry — queue depth, GPU-minutes per job, pick-rates — tunes the scheduler. The trade-off underneath is quality against GPU-minutes; nearly every knob here is one buying the other, and at each one I’ve taken the cheap, reversible side. With more time, two open gaps. Safety here is entirely per-prompt — nothing on this diagram looks across one account’s history, which is where a patient user mapping our thresholds would show up. And I’ve treated the output as silent: with audio, moderation and provenance both get a second half.

    Built in step 7: Progressive rendering and the CDN

What this teaches

Learn AI system design by building a Sora-style text-to-video service step by step. An interactive guide covering async job queues for minutes-long renders, prompt safety and LLM prompt expansion, diffusion transformers over spacetime latents, the GPU scheduling economics of video generation, draft-then-final progressive rendering, output moderation and C2PA provenance watermarking, and delivery through an asset CDN.

Key takeaways

  • Async jobs — job ID in ms, status stream, first-class cancel
  • Safety × 2 — cheap prompt screen, truthful pixel scan
  • Prompt expansion — LLM translates users into the training distribution
  • GPU scheduler — tiers, fair-share, preemptible batch, honest waits
  • Spacetime latents — compress ~100×, denoise jointly, decode last
  • Provenance — C2PA + invisible watermark on every render
  • Draft → final — cheap iteration, expensive only for the winner
  • Telemetry — GPU-minutes and pick-rates tune the whole economy

Concepts covered

  • Why video is a different beast than images
  • Async or nothing
  • Prompt safety, at the cheap end
  • LLM prompt expansion
  • Scheduling minutes-long GPU jobs
  • Diffusion transformers over spacetime latents
  • Output moderation and provenance
  • Progressive rendering and the CDN
RUN IT YOURSELF

Every frame matched the prompt and the clip is unwatchable

One 96-frame clip, one shared noise draw, and the identical per-frame sampler assembled four ways: every frame from the prompt alone, each frame conditioned on the one before it, keyframes plus interpolation, and one joint solve over the whole block. It scores each on four numbers — drift from the prompt, flicker in the attributes the prompt never specified, how far the subject’s identity wanders, and how much of the intended motion survived. Change FRAMES, hit Run, and watch exactly one row fall apart as the clip gets longer.

HOW TO READ THE CODE — 5 IDEAS
  1. Generating every frame independently gives the second-lowest prompt drift in the table (0.10) and the worst clip in it: flicker 1.32, 33× the joint run. Every frame is individually right about the prompt and no two agree with each other — step 5’s first option, priced.
  2. Conditioning on the previous frame drops flicker to 0.10, because the conditioning frame pins the attributes the prompt never pinned. The same line inherits that frame’s error, so the drift is a random walk: 0.19 at 12 frames, 0.68 at 96, 1.82 at 320. Change FRAMES and only that row moves.
  3. Keyframes plus interpolation look good on both error columns and delete a third of the action (motion 69%). The stride sweep puts the cliff at stride = PERIOD/2 — that is Nyquist, not a tuned constant, and the dips at 24 and 48 are keyframes landing on the same phase of the carve every time (step 5’s third option).
  4. The joint solve is handed nothing the independent mode was not — a test asserts the two are identical as the prior goes to zero — and still beats it on flicker and on drift (0.10 → 0.03), because it pools 96 observations where a per-frame decision reads one. That is what step 5 means by coherence being architectural rather than a post-process.
  5. Its prior penalizes acceleration, not change, which is why it can be smooth without being static — and is also the bill: it separates a 48-frame carve from per-frame noise only while that gap exists. Push LAM to 800 and the joint mode becomes the keyframe mode (motion 76%).
CPython · WebAssembly
built to be reasoned about, not memorized — make the calls, trigger the surge, run the quiz.
Finished this one? 0 / 65 AI System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More AI System Designs