Add the LinkedIn marketing video, rendered from code - #838
Draft
0-jake-0 wants to merge 13 commits into
Draft
Conversation
A 1080x1080, ~36s film for LinkedIn's muted mobile feed, carrying one message: one graph definition runs as both an instant historical backtest and a live realtime system, so backtest and production can't drift. Everything on screen is code in this repo or output captured from running it. Two rules hold the pipeline together: - **Pacing follows the voice.** `build-voice.py` synthesises one wav per scene with piper (LibriTTS-R, offline) and measures it; scene frame counts are derived from those durations and typed nowhere. That is the seam for dropping in a human voiceover later. - **Terminal output is captured, never written.** `capture-output.sh` runs the `odds_evens` example once per run mode and records what it printed, along with the command that printed it. It fails the build if the two modes ever stop producing identical values, because that is the claim the video makes. Scenes 4 and 5 show what the engine actually does rather than the tidier story. Under `HistoricalFrom(NanoTime::ZERO)` engine time is pure logic, so the replay stamps 0.000_000 onward; under `RealTime` engine time *is* the wall clock, so the same six ticks stamp as epoch nanoseconds. The timestamps are not identical across modes and the video does not say they are. What is identical, byte for byte, is the values and their order — so scene 5 dims the clock column and holds the value column bright, and the badge reads "same values, same order". `odds_evens` grows a run-mode argument to back that, following the convention `run_mode` already sets. The graph above it is wired once and does not know which mode it got, which is the point the example was already making and now demonstrates. Deliverables land in `marketing/linkedin-video/out/`: the MP4 and a sidecar SRT generated from the same measured timings as the burned-in karaoke captions, so the two cannot drift. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1
The film was built on a placeholder teal/amber palette and an invented diamond glyph. Both are replaced with the actual brand: the hexagonal-spiral mark from the Sept 2025 deck in `wingfoil-io/assets`, and the two colours sampled from its gradient — #FF31C9 magenta and #2C98FF blue, meeting at #5D78FF. The split earns its keep rather than just matching. **Magenta is live** — the wall clock, the drift, the thing that moves. **Blue is the engine** — the graph, the replay, the thing that doesn't. So the hook's two rules are magenta and blue drifting apart, scene 3 gives one branch each colour and makes `merge` the indigo between them, scenes 4 and 5 colour the engine-time column by run mode, and the payoff converges the two rules into a single rule painted in the brand gradient — the same sweep the logo is drawn in. Scenes 2–5 lose their headings. Each one mostly restated the caption underneath it, and dropping them buys the caption band 29px -> 38px, which is the size that matters for someone watching muted on a phone. The badges stay: they carry the run-mode claim rather than repeating the narration. wingfoil.io itself is blocked by the sandbox's egress proxy, so the brand came from the assets repo instead. `src/theme.ts` is the single place three hexes and one PNG define the look, and the README says so, so re-sampling against the live site is a two-file change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1
The pills under scenes 4 and 5 ("backtest · instant · deterministic",
"live · wall-clock · same values, same order") were a third voice saying
what the narration and the caption already said. Scenes 2-5 are now the
visual and one caption band, nothing else.
What tells the two run modes apart is the evidence rather than a label on
it, which is stronger anyway: the command line carries `-- realtime`, the
engine-time column is blue and zero-based for the replay and magenta and
epoch for the live run, and only the live run has a `waiting…` prompt. With
the pills gone the terminals take the room instead — 25px to 27px on the
replay, 22px to 24px on the live run.
The code scene's narration now introduces what it is about to show: "For
example: split a counter into odds and evens, merge them back, then run
it." Scene lengths, the caption timings and the SRT all re-derived from the
new wav, as they are supposed to.
36.9s.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1
The film demonstrated the mechanism on `1 is odd` / `2 is even`, which proves the plumbing and nothing about the capability. For an audience of quants the example *is* the argument, so it is now a real one. Adds `examples/core/top_of_book`: real NASDAQ AAPL messages from the LOBSTER sample, through a real limit order book, over a `channel` source. That last part is what makes the video's claim demonstrable rather than asserted — `channel` is the one source that works in both run modes, fed with `send_at` for a deterministic replay and with `send` for a live feed, so the identical graph really does run both ways. The producer is the only code that knows which mode it is in. The diamond is now load-bearing. `book` is a shared apex the engine runs once per cycle however many readers it has — rebuilding a limit order book per downstream path would be both wrong and expensive — and `bid` and `ask` run at genuinely different frequencies, so `distinct` on each branch and a `join` that fires when either moves is the natural wiring rather than a contrivance. Scene 3 now carries the README's published **~27 ns engine overhead per node cycle**, opposite the clock and landing while the narration is on "runs each node once per cycle". Latency is the differentiator this audience asks about first and the film previously never mentioned it. The payoff narrows from "backtest and live can't drift" to "**implementations** can't drift". Code cannot drift and that is provable; live-versus-backtest divergence is also driven by market impact, fills and data revisions, which this does not solve. The narrower claim is the one that survives contact with the audience. Captures assert quote-for-quote equality across both modes over the whole run, not just the six rows on screen. A live run reads the book at cycle boundaries, so under load it can coalesce updates a replay resolves separately; the README says so, and the assert is what stands behind the wording. 40.8s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1
The example reached for `g.channel::<Message>()` directly and then matched
on `RunMode` inside one producer function. That works, but it puts the
plumbing where the seam should be: a reader has to follow the channel to
see what actually differs between a backtest and production.
`MarketData::connect` is that seam now. `Replay` stamps each message with
its own time and lets the engine schedule it; `LiveFeed` waits out the gap
each message originally arrived after and hands it over; `market_data`
picks one, and is the only place in the program that branches on run mode.
Both still deliver over a `channel` — that is the one source working in
either mode — but it is an implementation detail of the two impls rather
than the thing the example is about. A real feed implements the same trait
and nothing below `connect` changes.
This is the shape `run_mode` already introduces, applied to real data.
Scene 2 gains from it. The snippet now opens on
// The only line that differs between backtest and live.
let feed = market_data(run_mode)?.connect(&g)?;
which is the film's whole argument in one statement, where before the first
line was channel construction. `market_data` reads `SPAN_SECONDS` from the
const rather than taking it as a parameter, so the call on screen is exactly
the call in the example.
Also replaces the example README's sample output, which was written from a
placeholder before the example ran, with what it actually prints.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1
The film read at 18-20 characters per second on six of its eight captioned sentences. Netflix caps English subtitles at 17 CPS and the BBC lands near the same place, and both assume a viewer whose only job is reading — this one asks them to take in a code block or a live terminal at the same time, so it was comfortably too fast for the muted viewer it is built for. `scripts/check-pacing.py` now measures every captioned sentence over its real on-screen window and fails the build over 15.5 CPS. `npm run build` runs it between the voice build and the render. The film peaks at 14.5 now. The fix was mostly `voice.lengthScale` 1.05 -> 1.37 and `sentenceSilence` 0.35 -> 0.7, plus a `hold` on the two scenes whose *final* sentence was the tight one, and splitting the longest realtime line in two. Wording was trimmed as well, but not for the reason it looks like: shortening a sentence shrinks its window along with its text and barely moves CPS at all. Trimming buys back length budget so the voice can slow without the film growing — the failure message says so, because it is the first thing anyone reaching for this will get wrong. Also pins `voice.noiseWScale` to 0. VITS predicts phoneme durations stochastically, so the same text synthesised twice differed by ~2% — enough to re-time every scene, shift every caption and make the new gate flaky. Pinned, `assets/narration.json` is byte-identical across runs, which the pipeline needed anyway to deserve the word re-renderable. 45.8s, up from 40.9s. Past the 45s sweet spot by a whisker and well inside the 60s cap — the right trade for captions a reader can actually finish. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1
Scene 3 carried "~27 ns engine overhead per node cycle", which is the weakest number the repo publishes: it is the interpreted engine's per-node cost, and it says nothing about the tier that makes wingfoil interesting. It now carries the nitro compiled figures from benches/README.md — 23.9 ns of whole-graph overhead per cycle, 12× the interpreted tier, 1610× tokio async streams. Those come from the depth-10 branch/recombine sweep, which is the same fan-out-and-recombine shape the scene animates beside them. Three things hold the claim honest, and all three are on screen. "Overhead" is load-bearing: 23.9 ns is what the engine adds per cycle, not what a node's own work costs, and maintaining a limit order book dwarfs it — the narration says "of overhead" for the same reason. The workload is named, because benches/README.md is emphatic that the ratio is the claim and the absolute time is not, so quoting one without its workload quotes it wrong. And it is not a claim about the graph in scenes 2, 4 and 5, which the scene never implies. The ratios take the indigo brand tone rather than magenta, which everywhere else in the film means "live". Narration and the pacing gate re-derived: `lengthScale` eases to 1.33 to pay for the longer line, and the dag scene's `hold` goes back up because the gate caught its final sentence at 16.8 CPS once the hold was trimmed. 46.5s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1
The claim was overreaching in two directions at once. "1610× tokio async streams" is a real published figure, but it comes from a depth-10 branch/recombine sweep — a workload built to expose per-path propagation, so it flatters wingfoil by construction. A ratio like that means nothing without its workload attached, a frame of video cannot carry that much fine print, and this audience is right to read a bare 1610× as a strawman. It is gone, along with any other third-party comparison: the tiers stand on their own. The remaining numbers are now *measured* rather than quoted. `scripts/capture-bench.sh` runs the committed `fanout` group of benches/tiers.rs, reads criterion's structured estimates, and writes `assets/bench.json`; `src/bench.ts` renders from that file. Nothing on screen is typed, and re-measuring on a quiet host is `npm run bench`. On this machine: 32.0 ns per graph cycle compiled, against 1.57 µs interpreted. Two judgement calls in what is *not* shown. The `legacy` bar the bench also measures is an internal regression baseline, not a claim. And the figure is per *graph* cycle rather than the 0.31 ns per node-cycle it divides to — roughly one CPU cycle per node invites the correct suspicion that the optimiser flattened part of a synthetic benchmark, and per-graph-cycle is both honest and something a reader can picture. Attribution matters here more than usual: 103 is the *benchmark's* node count, while the graph animated beside it is five boxes and twelve nodes (`g.snapshot().nodes.len()` on the example). The panel reads "fanout benchmark · 103 nodes" and the narration says "benchmark graph", because without that a viewer reads the figure as describing the graph on screen — wrong by an order of magnitude. Absolute times are machine-specific and this is a shared cloud host, which punishes the interpreted tier hardest and so flatters the ratio. The README says so, and says to re-run before leaning on the 49×. 48.9s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1
Every previous version of the perf claim measured something other than what
the viewer was looking at. The last one was honest about its own numbers but
measured benches/tiers.rs's `fanout` graph — 103 nodes — beside an animation
of a five-box diagram whose real graph is twelve nodes. The two before it
quoted figures rather than measuring, one of them a 1610x ratio against tokio
on a workload built to produce it.
The example now times its own replay and prints it, so the claim is about
this graph over this data:
46 quote changes
3.0s of market data replayed in 484.148µs — 6196× faster than real time
That line is the reason a deterministic replay is worth having, and it needs
no workload footnote, no tier caveat and no third-party comparison. It moves
to scene 4, under the terminal showing the run that produced it, and scene 3
goes back to carrying the structural point alone.
The capture treats it as a measurement rather than an anecdote: `--release`,
repeated (`REPEATS`, default 5), median recorded with the fastest and slowest
kept alongside. That mattered — a run taken while a render was finishing came
in at 0.84 ms against a 0.40 ms idle median, and a single sample would have
put whichever one happened first on screen. The speedup renders to two
significant figures, which is all the spread supports.
The pacing gate earned its keep twice more, catching the new historical line
at 18.8 CPS and again at 18.3 after a first attempt at splitting it.
Drops the now-unused bench capture, its parser and src/bench.ts.
44.9s.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1
"3.0s of market data replayed in 0.43ms — 6,196× faster than real time" was
unimpressive, and for a good reason: it measured how quiet the tape was, not
how fast the engine is. Three seconds of the LOBSTER sample is 324 messages.
Replaying a sparse feed quickly is not a claim, and a multiple of real time
is not the metric anyone in this audience compares.
The replay now takes the whole file, and reports throughput:
91997 messages, 15387 quote changes
replayed in 193.630ms — 475117 messages/sec
A full trading hour of AAPL — every message, through a real limit order book,
15,387 quote changes out — in about a tenth of a second. That is worth
quoting; 324 messages never was.
`LIVE_SPAN_SECONDS` now bounds only the *live* feed, because a live run is
paced by the wall clock and an hour is an hour. The replay waits for nothing,
so it takes everything. The quotes the two modes share are still identical —
the live window is a strict prefix of the replay — and the capture asserts
exactly that, plus that it really is a prefix rather than the whole thing.
`MarketData` grows a `len()` so the run can report how much work it did
without the graph knowing which feed it got.
45.3s.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1
0-jake-0
force-pushed
the
claude/wingfoil-linkedin-video-x8wm4r
branch
from
August 15, 2026 14:34
4314583 to
f3a6324
Compare
Four versions of a speed claim were built and all four were withdrawn. The last was measured, on the example itself, at a scale worth measuring — and it was still wrong, for a reason that rules out the category rather than the number. Decomposing a 156 ms replay of the hour: ~56 ms is the `lobster` order book, ~80 ms is writing 15,387 lines to stdout, ~23 ms is wingfoil. A headline figure there is 85% a measurement of things that are not the framework. The confirming test came from the other side. Swapping `println!` for a 64 KB `BufWriter` moved the "engine" number by 30% (156 ms -> 110 ms) without touching the engine. If the sink moves your number by a third, the number was never about the framework. For the record, `log` and `tracing` are both *slower* than `println!` when enabled (169 ms, 174 ms) — each adds a timestamp, level and target on top of the same syscall per record — and both are ~free when disabled, which is the actual argument for `logged` as a tap. That is not a wingfoil problem; it is what a realistic workload looks like. Payload and I/O dominate, engine dispatch is the small term, and a marketing frame cannot carry the caveats that would make a throughput figure true. The engine's own numbers belong in benches/README.md, where the method sits next to them. The claims the film keeps — one definition, identical quotes in identical order, only the clock differs — are ones the capture script asserts on every build. Also makes the voice-swap seam real rather than described. `scripts/import-voice.py` takes one wav per scene, measures each, copies them into public/ and rewrites narration.json, so a human recording re-times the film with nothing in src/ touched. Word timings are apportioned by default and read from a `<scene>.words.json` when one exists — the shape @remotion/install-whisper-cpp emits — so word-level karaoke is an upgrade per scene rather than all or nothing. 43.4s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1
`--from ~/vo` was a placeholder path in the instructions, and pointing it at a directory that does not exist gave a bare "missing hook.wav" — which reads as a broken tool rather than "you have not recorded anything yet". It now explains that the path is yours to create, and prints the seven lines with the filename each belongs in. `--script` does the same on demand, so the lines can be read off the terminal while recording rather than dug out of script.json. Also documents the two things that bite: wavs must be uncompressed PCM, and the importer copies them verbatim so levelling is the recordist's job.
Recording seven separate files is a chore, and it was the only path on offer. `split-voice.py` takes a single continuous take and cuts it on the pauses between lines: RMS over 20ms frames, runs of speech separated by at least --gap of near-silence, no dependencies beyond `wave`. It will not write anything unless it finds exactly as many segments as there are scenes, and it prints each one beside the line it will become, so a mis-detection is caught by eye rather than by a render. Without --to it only reports. The two knobs when the count is wrong (--gap, --floor) are named in the failure. Verified by round-trip: the seven synthesised wavs concatenated with 1.3s gaps into one 48s take, split back, and imported — seven for seven, correctly aligned. That round trip also surfaced something worth documenting: a trimmed recording loses the lead-in, tail and inter-sentence padding the synthesised build adds, so the pacing gate is stricter on imported audio. `--hold` recovers some of it but only widens a scene's *last* sentence, which is why a short opening line still wants real word timings.
0-jake-0
marked this pull request as draft
August 16, 2026 13:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A 1080x1080, ~36s film for LinkedIn's muted mobile feed, carrying one
message: one graph definition runs as both an instant historical backtest
and a live realtime system, so backtest and production can't drift.
Everything on screen is code in this repo or output captured from running
it. Two rules hold the pipeline together:
build-voice.pysynthesises one wav perscene with piper (LibriTTS-R, offline) and measures it; scene frame
counts are derived from those durations and typed nowhere. That is the
seam for dropping in a human voiceover later.
capture-output.shrunsthe
odds_evensexample once per run mode and records what it printed,along with the command that printed it. It fails the build if the two
modes ever stop producing identical values, because that is the claim
the video makes.
Scenes 4 and 5 show what the engine actually does rather than the tidier
story. Under
HistoricalFrom(NanoTime::ZERO)engine time is pure logic, sothe replay stamps 0.000_000 onward; under
RealTimeengine time is thewall clock, so the same six ticks stamp as epoch nanoseconds. The
timestamps are not identical across modes and the video does not say they
are. What is identical, byte for byte, is the values and their order — so
scene 5 dims the clock column and holds the value column bright, and the
badge reads "same values, same order".
odds_evensgrows a run-mode argument to back that, following theconvention
run_modealready sets. The graph above it is wired once anddoes not know which mode it got, which is the point the example was
already making and now demonstrates.
Deliverables land in
marketing/linkedin-video/out/: the MP4 and asidecar SRT generated from the same measured timings as the burned-in
karaoke captions, so the two cannot drift.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_011boKQCBTFCkTZijPdPdnL1