Back to Insights
17 Jun 20263 min readBuild in Public

A Demo for Every Feature — and the Bug Where Two Voices Talked Over Each Other

We have a rule: nothing is "done" until there's a sixty-to-ninety-second video of it actually working. Not a screenshot. Not a description. A real recording — the terminal or the browser, doing the thing, narrated.

That rule does something sneaky. It turns "is this finished?" from an opinion into a deliverable. You can argue about whether a feature is complete. You can't argue with a video that either shows it working or doesn't. The demo is the acceptance test that a human can watch.

How the pipeline is built

The whole thing is a function: a small storyboard file goes in, a finished mp4 comes out. The storyboard describes a sequence of cards — a title card, a few terminal or browser segments showing the real flow, a closing card — each with a caption and a line of narration.

Under the hood it's deterministic shell-outs, not magic:

  • Terminal segments are recorded through a scripted virtual terminal, so the "typing" is reproducible and never depends on a human hitting keys. (Workers have no screen to record — the demo has to be capturable headless.)
  • Browser segments are driven by a real browser under automation, recording itself as it clicks through the flow.
  • Narration is generated text-to-speech, one short clip per card.
  • Cards and captions are rendered as images and composited on top.
  • ffmpeg stitches it all together — segments end to end, narration mixed in, captions burned on.

One command, resumable. If text-to-speech hiccups on one card — and cloud TTS throws the occasional transient error — you rerun and it skips everything already done, retrying only the part that failed.

The bug: two voices, one timeline

Here's the gotcha that cost us a confusing afternoon of "why does this demo sound haunted."

Each card has a duration, and each card has a narration clip placed at the card's start. The mux step takes every narration clip, delays it to its card's start offset, and mixes them all onto one audio track. Straightforward.

Except: the mux step trusts your durations completely. It does not stretch a card to fit its narration, and it does not trim narration to fit its card.

So if a card is three seconds long but its narration is five seconds long, those extra two seconds of audio don't get cut. They bleed forward — and start playing on top of the next card's narration. Two voices, talking over each other, slightly out of phase. It sounds exactly as bad as it reads.

The fix is a one-line invariant we now enforce on every storyboard:

Each card's duration must be ≥ its narration length (plus about a second of pad).

The pipeline already logs the length of every narration clip and warns when a clip outruns its card — we'd just been ignoring the warning. Now we read it. Terminal segments are usually long enough on their own; it's the short title-and-transition cards, paired with a wordy line of narration, that overrun.

The "burn the captions in" tax

One more constraint worth naming, because it shaped the design. The box these demos render on can't draw text directly onto video frames. So captions and lower-thirds can't be a live ffmpeg text filter — they have to be rendered as images first and overlaid.

That sounds like a limitation, and it is, but it forced a discipline we'd keep anyway: every caption is a real composited image, styled consistently, on-brand, every time. There's no "eh, the default font is fine." The captions are designed, because they have to be made deliberately to exist at all. The final pass always burns them in and trims dead air, and someone watches the whole thing through once before it counts.

Why bother

Because a demo per feature changes the incentives. When "done" means "I produced a watchable proof," people stop hand-waving the last ten percent — the ten percent that's usually where the bug is. The video catches the thing the unit test didn't, because the video is the integration test a human can sit through.

And the meta-joke writes itself: the most common bug in our demo pipeline was an audio race condition. Even the tool that proves your features work has features that can break. So we demo the demo tool, too.

We're documenting these as we go. If you're trying to make "show me it works" a default instead of an afterthought, we're happy to compare notes.

demosffmpegvideoprocessTTS

Found this useful?