Overlay Pipeline — (video × variant × chunk)
Last Updated: 2026-07-21
This page covers libs/flight-overlay/ (@rocket-club/flight-overlay), the
shared render model behind the Flight Deck, the
render jobs, and the Video Overlay Lambda.
ADR-0003 (docs/adr/0003-render-pipeline-variant-chunk.md) is the decision of
record; this page describes what shipped.
One renderer, three hosts — identical pixels
The lib's header states the contract verbatim
(libs/flight-overlay/src/index.ts):
One renderer, three hosts: the browser preview, the local CLI, and the Lambda. They must produce identical pixels.
The three consumers run on three different hosts — the browser preview/sync
wizard (native canvas), the local CLI, and the Lambda handler (@napi-rs/canvas)
— and must render identical pixels, because the preview's whole job is to
promise what the render will produce. A "Boost" clip must be byte-identical to
the "Boost" chart button. The only way to guarantee that is one implementation
everyone imports; a variant or a chunk is a parameterisation of that single
buildOverlayModel / buildPhasePresets / resolveChunk path, never a second
phase-math or render implementation.
The overlay-variant axis
An overlay variant is a named preset of buildOverlayModel inputs plus a
little render-time presentation. Variants are a closed, code-defined enum —
not user-authored content — living in libs/flight-overlay/src/model/variants.ts:
OVERLAY_VARIANTS— the seed catalogue.OverlayVariantId— the id union.getOverlayVariant(id)/listOverlayVariants()— the lookup and browse helpers.
Each variant resolves to a partial BuildOverlayOptions (its build field) that
buildOverlayModel (model/build.ts) consumes; units is always pinned, and
the window knobs default in the builder. The four seed variants are:
full-band-metric— Full band (metric)full-band-imperial— Full band (imperial)minimal-ticker— Minimal tickerboost-closeup— Boost close-up
Adding a variant is a data edit to that array — single-tenant and low-ceremony by
design, not an authoring subsystem. The app imports the catalogue through the
thin passthrough apps/web/src/lib/flights/overlay-variants.ts.
The chunk axis
A chunk selects a [t0, t1] window in flight time (t = 0 at liftoff). The
Chunk / ChunkId types live alongside the variants in model/variants.ts
(the neutral lib), because variants.ts references them and both the app and the
Lambda import them, never the reverse:
type ChunkId = 'full' | 'boost' | 'apogee' | 'descent' | 'custom'
full is the whole trim window (not a phase sub-range); the four named ids
(boost/apogee/descent) resolve through the same buildPhasePresets the
telemetry charts use. That phase math lives in
libs/flight-overlay/src/model/phases.ts — the app's
components/charts/telemetry/presets.ts is now a thin re-export of it — so a
rendered "Boost" clip is range-identical to the "Boost" chart button.
Chunk normalisation and resolution ship in the lib at model/chunk.ts:
normalizeChunk(raw)collapses the loose transport shape ({ id, t0?, t1? }, shared by the render-start input and the Lambda event) into the canonicalChunkunion — it rejects an unknown id and acustomchunk lacking finitet0 < t1, and drops stray bounds on the named ids, so exactly one validated shape reaches the resolver.resolveChunk(chunk, window, markerTimes)turns aChunkinto a concrete[t0, t1](ornullwhen the chunk is unavailable/empty for the flight), reusingbuildPhasePresetsfor the named ids.
Note:
resolveChunk/normalizeChunkand theChunk/ChunkIdtypes ship in the lib (libs/flight-overlay/src/model/), so the app and the Lambda call one function with no second phase implementation to drift. ADR-0003 sketched the resolver app-side; its status header records that open question as resolved to theflight-overlaylib, which is what shipped.
A named chunk whose driving marker is absent (e.g. no Apogee marker) is simply not offered — the same availability rule the chart toolbar follows.