Haute Lumière · Édition Nº 10 · A build brief for Claude Code
The Reel
A still becomes a movement. A voice becomes a picture. Eight-second shards become one continuous thing that looks like somebody meant it.
The edition
What this document is for
This is the whole specification for a program called The Reel — a local video studio that turns photographs into motion, audio into picture, and short generated shards into long continuous clips. It is written to be handed to an implementer and built from, not admired.
It is organised into five parts. The first argues why the thing should exist at all, which matters because the argument determines every later decision. The second designs the surface a person actually touches — the part that has to feel like a Haute Lumière object rather than a research demo. The third documents the open-source machinery underneath, honestly, including the parts that are currently fragile. The fourth attacks the one genuinely hard technical problem: continuity across generated shards. The fifth is the build order, the ship test, and the failure modes.
A note on tone, since it is load-bearing. The engineering culture that produced most of this stack builds interfaces that read like a punishment. Nine collapsed panels, a node graph, forty numeric fields with no defaults and no explanation, and somewhere in there a checkbox that silently determines whether your output is watchable. That is not a taste failure. It is a values failure — the assumption that if you cannot read a scheduler's name you do not deserve to make anything. The Reel inverts that assumption without dumbing anything down. Everything remains reachable. Almost nothing is presented.
- Working title
- The Reel
- Platform
- macOS, Apple silicon, unified memory
- Interface
- Local web app · Haute Lumière, seven registers
- Engine
- ComfyUI (headless) · Wan 2.2 · FFmpeg
- Dependency
- None that requires a network
- Status
- Engine tuning · interface working
Part I
The Argument
Why this exists
Every generative video model available today produces roughly five seconds of footage, and then stops. This is not a bug anyone forgot to fix. It is a consequence of attention cost scaling with the square of the frame count, of training clips being short because storage and compute are finite, and of the fact that a model asked to hold a coherent world in mind for twenty seconds will lose the thread somewhere around six.
So the ecosystem produces shards. Beautiful, occasionally astonishing shards, each one about as long as a held breath, each one arriving with its own colour temperature, its own idea of what your subject's face looks like, and its own private opinion about which way the light is coming from. Put three of them end to end and you do not get fifteen seconds of film. You get three unrelated advertisements for the same vague mood.
The models are not the bottleneck any more. The joinery is.
The eight-second problem
State it precisely, because a precise statement is most of the solution. Given a generator that emits N seconds of coherent motion, produce kN seconds of motion that a viewer reads as one continuous take — or as a deliberate cut, which is different from an accident. The failure modes are not subtle and they are not random. There are exactly three, they compound, and each has a known countermeasure that nobody bothers to wire up because wiring it up is boring and publishing a new sampler is not.
The first is identity drift. By shard four your subject has a different jawline. The model was never told that this is the same person across cuts; it was told, four separate times, to make a person who resembles a text description.
The second is photometric drift. Each shard's exposure, white balance and contrast curve land wherever the sampler happened to leave them. Concatenated, they strobe. This is the most visible failure and the easiest to fix, which is a fair summary of the state of the field.
The third is kinematic discontinuity. Shard one ends with an arm rising. Shard two begins with the arm already down. No amount of colour matching rescues a body that teleports.
What you are actually asking for
Three verbs, and it is worth being blunt that they are three genuinely different programs sharing one chassis.
A still becomes a movement. You have a photograph you like. You want it to breathe — the fabric to move, the hair to lift, the light to shift across a face. This is image-to-video conditioning, and it is the most reliable of the three because the model is given an anchor it cannot argue with.
A voice becomes a picture. You have audio — a passage read aloud, a piece of music, a meditation. You want picture that belongs to it: cut on the beat, paced to the breath, shifting when the speaker shifts. This is not video generation with a soundtrack bolted on afterwards. It is audio analysis driving the generation schedule, which is a different architecture and almost nobody builds it.
Shards become a clip. You have a folder of eight-second fragments, generated over several evenings, of roughly the same subject. You want one thing out of them. This is the joinery problem above, and it is where the actual product lives.
Continuity is the product
This is the thesis, so it gets its own heading. The Reel is not a video generator. Generation is a commodity — there will be a better open weight next quarter, and the quarter after that, and the program must be indifferent to which one is installed. What is not a commodity is the machinery that makes generated material behave: the anchoring, the matching, the interpolation, the identity lock, the seam repair, the ledger that records which seed produced which frame so that a good result can be reproduced rather than mourned.
Which means the architecture has one non-negotiable property. The generator is a replaceable part. Wan 2.2 today, something else in six months, and the continuity layer does not notice. Anything that hard-codes a model's node names into the interface has already failed.
Why it has to be local
Four reasons, in descending order of how often they are admitted out loud.
Cost. Continuity work is iterative by nature — the fourth attempt at a seam is normal, not a failure. At API prices, iteration is rationed, and rationed iteration produces exactly the shallow work the tools are accused of producing. On your own silicon the marginal render costs electricity, which means you can afford to be a perfectionist.
Latency of thought. A queue you own returns work while you are still holding the intention that produced it. A queue you rent returns it after the intention has gone.
The material. Some of what goes through this program is not for anyone else's servers, and that should require no further explanation.
Permanence. A hosted model is deprecated on somebody else's schedule. Weights on your disk are not. A project that takes eight months should not have its engine retired in month five.
Part II
The Exterior
The part a person touches
The interface has one job, and it is not to expose capability. It is to make the next action obvious while keeping every capability one deliberate gesture away. A node graph does the opposite: it exposes everything and suggests nothing, which is why it feels like homework.
The Reel opens on a single dark surface — Midnight or Nocturne by default, because this is night work — with one sunburst breathing in the upper right on a twelve-second cycle and paper grain at three and a half per cent. Cormorant Garamond for anything a human wrote. Manrope, tracked to a fifth of an em, for anything the machine is reporting. Nothing bounces. Nothing pops. Transitions run four hundred milliseconds on a reader's easing curve, and the only accent colour in the entire program is gold.
Four doors, one room
The home surface offers four doors, each a full-bleed plate with one line of serif over it. No icons, no grid of tools, no dashboard.
Door one
Give it a picture
Drop a still. Choose how it should move — a breath, a drift, a turn, a wind. It renders. Image-to-video, and the door that works first.
Door two
Give it a voice
Drop audio. The program listens before it renders: beats, breaths, phrase boundaries, the shape of the silence. Picture is then built to that shape.
Door three
Give it the shards
Point at a folder. The program reads every fragment, proposes an order, and offers to make them one thing. The joinery door.
Door four
Open the Spool
Everything you have ever made, with its seeds, its settings and its lineage. Not a file browser — a record.
Behind each door is the same three-step rhythm, and the rhythm never varies: give it something · say what you want · watch it arrive. The second step is a serif text field at nineteen points, because the cursor falls into a serif in this system and always has. Not a form. A sentence.
Everything is reachable. Almost nothing is presented.
Under the sentence sits one hairline labelled the particulars. Closed, it is a line of text. Opened, it is every parameter in the graph — sampler, steps, cfg, shift, precision, resolution, frame count, seed — each with its current value, its native default, and one plain-English sentence about what moving it does. Not a tooltip. A sentence, in the body serif, at reading size. The expert loses nothing. The beginner is not assaulted.
The Spool
The Spool is the program's memory and the reason it is worth using twice. Every render is an entry: the output, the input that produced it, the full parameter set, the seed, the model hash, the wall-clock duration, and a lineage pointer to whatever it was derived from.
It presents as a vertical film of stills rather than a table — hover to see it move, click to open it, and beside it in Manrope the facts that would let you make it again. Two gestures matter more than the rest. Again, differently re-runs with one parameter nudged and the seed held, which is how you actually converge on a look. Again, exactly re-runs bit-identical, which is how you prove the pipeline is sound rather than lucky.
Every entry can be dragged into a strip at the bottom of the window. That strip is the clip you are building. This is the whole editing model, and it is deliberately thin: order, trim, and the joinery decisions from Part IV. This is not a non-linear editor and must never grow into one — there are excellent ones already and the point of the program is the part they cannot do.
The Ledger
A quiet horizontal band, always visible, never louder than the work. It carries what the machine is doing in one honest line of serif and four figures in Manrope: what is rendering, what is queued, what the memory ceiling looks like, and how long the last one took.
Three rules govern it absolutely. It never uses red — a slow render is not an emergency and the body should not be told it is one. It never shows a progress bar that lies; either it knows the step count or it says working and shows the breathing dot. And it never blocks. The interface stays fully usable while the machine is busy, because a program that goes rigid during a four-minute render has decided its own convenience matters more than your afternoon.
The Hush
The signature state, inherited from the design system and non-negotiable. When something wants full attention — a finished render, a seam being judged, a plate open at full size — the rest of the surface takes a twelve-pixel backdrop blur and an ink veil at twenty per cent. Never black. Never fully occluded. Tap outside and you are back.
The Hush is where you evaluate. It is the difference between glancing at a result and actually looking at it, and the whole reason it exists is that judging your own work requires a state, not just a screen.
Queue and hold
Long renders are the normal case, not the exception, so the program is built around absence rather than attendance.
You queue as much as you like, in any mix of the four doors, and close the laptop. The engine runs under a persistent agent and survives the window being shut, the app being quit, and — with a resumable queue on disk — the machine being restarted. Finished work waits in the Spool with a single gold dot against it, the dot being the entire notification system. No badge. No count. No sound.
And one deliberate omission that took discipline: there is no percentage on the render itself. A percentage invites you to watch, watching is worthless, and worthless watching is how an evening disappears. The Ledger tells you it is working. Go and do something else.
What is never shown
The design is defined as much by its refusals, so they are enumerated rather than left to taste.
No node graph. Not hidden behind a tab, not available in an advanced mode. The graph is a compiler target, not a user surface. If a capability can only be reached by wiring nodes, that capability is not finished.
No model names in the primary interface. Wan 2.2 TI2V 5B fp16 is a fact about the installation, and it lives one level down with the other facts. The door says give it a picture.
No raw tensor errors. Every failure is caught and translated. RuntimeError: Invalid buffer size becomes this asked for more memory than the machine has — here is the largest size that will fit, with a button that sets it.
No terminal, ever. The stack underneath is command-line software. That is an implementation detail and the user's business exactly never.
No urgency surface. No streaks, no counters, no trending, no nudges. Nothing in the program asks to be opened.
Part III
The Underbelly
The open-source machinery
Everything below the surface is other people's software, and that is the point rather than an embarrassment. The open weights and open tooling of the last two years are extraordinary, they are free, and they run on a desk. What they lack is not capability. It is composition — somebody deciding which twelve of the four hundred available knobs actually matter and setting the rest correctly, once, permanently.
ComfyUI as a library, not an application
ComfyUI is the practical execution engine for open video models: it has the node coverage, the model support arrives there first, and its memory management on unified-memory Macs is better than anything hand-rolled. Its interface is also precisely the thing this program exists to spare you.
So it runs headless, as a local service on a private port, and The Reel talks to it over its HTTP API and its websocket. The Reel composes workflow JSON, posts it to /prompt, listens for progress on the socket, and reads finished frames off disk. The user never sees the port, the queue, or the graph.
Two consequences worth stating plainly. Every future model that gains ComfyUI support becomes available to The Reel by writing one new graph template, not by rebuilding anything. And the entire machine is inspectable — when something is wrong you can open the same graph in ComfyUI's own interface and look at it, which is exactly how the geometry bug below got found.
The models, and the geometry lesson
Wan 2.2 is the current engine — Alibaba's open video family, and the strongest open weight available for this work. Two variants matter.
| Variant | Native geometry | Memory | Use |
|---|---|---|---|
| TI2V 5B | 1280 × 704 · 121 frames · 24 fps | ~18 GB | The working engine. Text and image conditioning, roughly five seconds. |
| I2V / T2V 14B | 1280 × 720 · 81 frames | ~32 GB + | Higher fidelity, materially slower. For finals on a machine with the headroom. |
Supporting cast: umt5-xxl as text encoder, quantised to fp8 to keep it off the memory budget; the Wan 2.2 VAE for latent decode; uni_pc as sampler with the simple scheduler, cfg 5, shift 8 — which are not folklore, they are the shipped template defaults and should be treated as such.
Now the lesson, because it cost several evenings and generalises to every model that will ever replace this one.
The finding
Video diffusion models are not resolution-agnostic. Asked for 640 × 352 at 25 frames, a model trained at 1280 × 704 by 121 frames does not return a smaller version of its competence. It returns mush — soft, smeared, structurally confused output that reads exactly like a precision bug and is not one.
The reason is architectural. Positional embeddings, the temporal compression ratio of the VAE, and the attention window were all learned at one geometry. Move far off it in either space or time and you are asking for extrapolation the weights never learned. Image models tolerate this reasonably well. Video models, whose latents are compressed along time as well as space, do not.
Two engineering rules follow, and they belong in the code rather than in anyone's memory. Render at native geometry, always — downscale afterwards with FFmpeg, which is free and correct, rather than asking the model to work small. And store native geometry as a property of the installed model, so that a future engine brings its own numbers and the interface simply offers what that engine is good at.
This is also, incidentally, the correct diagnostic method: when output is wrong, load the vendor's own template from disk and diff it against yours. Every parameter matching except frame count and resolution is not a small difference. It is the whole answer.
Precision on Apple silicon
Apple's Metal backend for PyTorch — mps — is fast and mostly excellent, and has two sharp edges that will each cost an evening if they are not handled once and written down.
fp16 accumulation. Some reductions on MPS accumulate in half precision where CUDA would use fp32, and in a long sampling chain the error compounds into visible softness or, occasionally, NaN. The fix is not a graph change. It is a precision flag on the diffusion model specifically — fp32 accumulation while keeping fp16 storage — and it costs some speed and buys correctness. Where the model supports bf16, prefer it; the wider exponent is more forgiving than fp16 at nearly the same cost.
Unified memory is shared, not dedicated. There is no separate VRAM pool to fill. The model competes with everything else running, including the browser displaying the interface. So the engine gets an explicit ceiling, sequential model loading, and aggressive unloading between queue items. And the interface never offers a size the machine cannot hold — the largest fitting geometry is computed from actual available memory, not guessed.
Whichever precision configuration wins is then baked into the launch agent, so it survives restart and is never rediscovered. That is the entire deliverable of a debugging session: not a working render, a permanent one.
The audio chain
Door two is the one nobody else builds properly, so it gets real machinery. Audio is analysed before anything is generated, and the analysis drives the generation schedule.
Structure
librosa · aubio
Beat and onset detection, tempo, and — more useful for spoken material — phrase boundaries and the shape of the silences. This yields the cut list before a single frame exists.
Speech
whisper.cpp
Local transcription with word-level timestamps. Gives the words, which condition the prompt, and their timing, which conditions the pacing.
Separation
Demucs
Stems, so picture can follow one element rather than the mix — cut on the percussion, breathe with the pad, hold on the voice.
Envelope
RMS + spectral flux
A per-frame intensity curve, resampled to the video frame rate, driving motion amount so that quiet passages move quietly.
The output of the chain is a schedule: a list of shots with durations that land on real musical or breath boundaries, each with a motion-intensity target and a prompt derived from the transcript at that moment. Only then does generation begin. This is the inversion that matters — picture serving audio, not audio decorating picture.
The stitcher
FFmpeg does all muxing, concatenation, colour conversion, downscaling and final encode, and it should be trusted completely — it is the most thoroughly proven media software in existence. Final delivery is H.264 in an MP4 for compatibility and ProRes for anything going into a real edit, and the audio is muxed once at the end from the original file, never re-encoded through intermediates.
Between shards sits frame interpolation — RIFE for speed, FILM where quality justifies the wait. Its job is not slow motion. It is generating the two to four frames that bridge a seam, so a joint reads as continuous motion rather than a jump. This is the single highest-leverage component in the whole program and it is four hundred lines of glue.
Optional and last: Real-ESRGAN for upscaling from native geometry to delivery size, applied once to the finished clip rather than per shard, because per-shard upscaling introduces its own inter-shard inconsistency and undoes the work of Part IV.
The graph compiler
The layer that makes the rest replaceable, and the piece to build most carefully.
The interface never constructs workflow JSON. It emits an intent — a small declarative object naming the door, the inputs, the schedule, the continuity requirements and the quality target. A compiler turns that intent into a concrete ComfyUI graph, choosing the template for the installed model, filling native geometry from the model's own record, and applying the precision configuration for the current hardware.
intent {
door: shards → clip
inputs: 12 fragments, ~8s each
schedule: from audio/breath-01.wav
continuity: identity locked · photometry matched · seams interpolated
quality: native geometry · fp32 accumulation
deliver: ProRes + H.264, 1920 × 1080
}
Three properties earn the abstraction. Intents are portable — the same one runs on any engine that has a template. Intents are the Spool's unit of record, so what gets stored is what you meant, not just what was executed. And intents are diffable, which is what makes again, differently a one-line operation instead of a re-derivation.
Part IV
Continuity
The only hard problem
Everything up to here is composition and taste. This part is the actual invention, and it is where the program either justifies itself or does not. The question is narrow: given shards that were generated independently, how do you make them behave as though they were not?
Six mechanisms, applied in order. Each is individually well understood. Nobody ships them together, which is the whole opportunity.
The handoff
The foundational trick, and the one everybody knows: the last frame of shard n becomes the conditioning image for shard n+1. The generator is given the world it must continue from rather than asked to invent a compatible one.
It works, and it decays. Each generation introduces small error, the next inherits it as ground truth, and by the sixth shard you are watching a photocopy of a photocopy — softening, colour creep, and a subject slowly becoming somebody else's cousin. Three refinements arrest it.
Anchor, don't chain. Every shard is conditioned on both its predecessor's final frame and a single fixed anchor frame chosen at the start. The chain provides local continuity; the anchor prevents cumulative drift, because the error cannot compound against a fixed reference.
Hand off a clean frame. The literal final frame is the worst frame in the shard — it carries the most accumulated sampling error. Hand off frame n−3 instead and let interpolation cover the gap. Nearly free, and startlingly effective.
Hold the seed family. One seed per project, offset deterministically per shard, rather than a fresh random seed each time. Shards then share a noise character, which reads as a shared visual grammar even where content differs.
The three drifts
Each drift from Part I, with its countermeasure.
Photometric drift is the most visible and the cheapest to fix. Compute a colour transform from each shard's histogram to the anchor's — per channel, in a perceptual space rather than raw RGB — and apply it. This is a lookup table, it costs nothing, and it removes most of what makes stitched footage look stitched. Do it before anything else, because it also makes the remaining problems easier to see.
Kinematic discontinuity is handled by the handoff above plus optical flow at the seam. Measure the flow field in the last frames of the outgoing shard and the first frames of the incoming one; where they disagree beyond a threshold, the interpolator gets more frames to work with and the transition is stretched. Where they disagree wildly, the program stops trying to hide it and proposes an honest cut instead — which is often the better film anyway, and admitting that is more useful than a smeared morph.
Identity drift is the hard one, and it gets its own section.
The identity lock
Three approaches, in ascending order of cost and effectiveness. The program should offer all three and default to the middle one.
Reference conditioning is free and weak: pass the anchor frame as an additional conditioning input at low weight on every shard. Helps. Insufficient alone past about four shards.
A project LoRA is the right default. Train a small low-rank adapter — a few hundred steps, twenty minutes on the same machine — on the handful of frames that define your subject. Every shard then generates through it. This is the single largest quality win available in the entire program, and it is absent from every consumer tool because it requires a training step and training steps frighten product managers. It should be one button labelled lock this face.
Post-hoc face restoration is the fallback: detect faces per frame and re-synthesise them against the anchor identity. Effective, and it will occasionally produce the uncanny stillness of a face that no longer quite belongs to the head. Offer it, do not default to it, and let the Hush be where that judgement is made.
The seam pass
The final mechanism, and the one that separates a tool from a craft instrument. After assembly the program runs a seam pass: it examines every joint and scores it.
The score is composite and deliberately simple — photometric distance across the joint, flow disagreement, identity embedding distance, and structural similarity over the bridging frames. Each joint gets a number and a verdict: clean, bridged, or wants a cut.
That verdict surfaces in the Spool as a row of dots along the strip, one per joint, gold where clean and hollow where not. Click a hollow one and the Hush opens on that joint alone, looping four seconds across it, with three offered actions: bridge harder, regenerate the incoming shard from a nudged seed, or accept it as a cut and let the program add the small photometric shift that makes a cut read as intentional.
A program that finds its own bad seams and shows them to you is worth ten that quietly ship them.
This is the feature to demonstrate first to anyone asking what the thing is for. Not the generation — everyone has seen generation. The self-audit.
Part V
The Build
Order of operations
Build in an order where each phase is independently usable, because a phase that only pays off after the next one is a phase that gets abandoned. Every one of these ships something you would actually open.
Five phases
Phase one
One frame, correct
Headless ComfyUI under a launch agent. Native geometry. Precision settled and baked in. Door one only, one render at a time. Ships when a still moves and looks right.
Phase two
The Spool and the queue
Intents, the record, resumable queue on disk, again-exactly and again-differently. Ships when you can close the laptop mid-render and lose nothing.
Phase three
The joinery
Door three. Handoff, anchoring, photometric matching, interpolation, the seam pass. Ships when twelve shards become one clip a stranger does not question.
Phase four
The ear
Door two. The full audio chain, schedule-driven generation, muxed delivery. Ships when picture lands on the beat without anyone placing it there.
Phase five
The lock
One-button project LoRA, and the 14B path for finals. Ships when a face survives twenty shards.
Phase three before phase four is deliberate, and the ordering is the argument of this document restated as a schedule: the joinery is the product, so the joinery gets built before the second entrance to it.
The twelve-point ship test
Run before any phase is called finished. Any single failure is a blocker.
- Does it render at the model's native geometry without being asked?
- Does a render survive the window closing, the app quitting, and a restart?
- Can any past result be reproduced bit-identically from the Spool?
- Is every parameter reachable in one gesture, and none of them in your face?
- Does every failure arrive as a sentence a person can act on?
- Is there any red, or any progress bar that does not know the answer?
- Does the interface stay usable while the engine is at full load?
- Does the program find its own bad seams before you do?
- Would it all still work with the network cable pulled?
- Could the generator be swapped without touching the interface?
- Does every string sound like the same editor wrote it?
- Is there anywhere in it that could make a person feel rushed?
What breaks
Soft or smeared output. Geometry first, precision second — in that order, because geometry is the more common cause and the cheaper test. Diff against the vendor template before touching anything.
Out of memory mid-queue. The ceiling was optimistic or something else grew. Sequential loading, unload between items, and recompute the largest fitting geometry from live free memory rather than a constant.
Drift past shard six. Anchoring is not on, or the anchor weight is too low. Verify the anchor is the fixed frame and not the rolling one — this is the most common wiring error in the whole design.
Strobing across joints. Photometric matching is running after interpolation instead of before it. Order matters: match, then bridge.
A model update changes everything. Which is why templates carry their own geometry and precision record. Pin the model hash in the Spool so that yesterday's good result remains explicable tomorrow.
And the honest closing note. The pipeline is composition, not invention — every component exists, is free, and works. What does not exist is the arrangement: an object that treats a generative model as a replaceable engine, continuity as the actual product, and the person operating it as someone whose attention is worth protecting.
That object is about six weeks of careful work, and nobody has built it because the people with the models are not designers and the people who can design are waiting for permission. There is no permission. There is a machine on the desk and a folder of eight-second shards that want to be a film.
Colophon
Set in Cormorant Garamond and Manrope. Seven registers, of which this document opens in Midnight. Twelve plates, each appearing once. Printed to A4 and to Letter without a build step.
A still becomes a movement. A voice becomes a picture. The shards become one thing.