← back DynOSTiC Updates Feb 7, 2026So we built our cutscene stack the only reasonable way: as a systems engineering problem wearing a storytelling costume.
Design Constraint: Same Timeline, Everywhere
The core requirement was simple to describe and annoying to implement:
- A timeline should behave the same in CLI, runtime preview, and save/resume.
- Validation should be strict enough to catch authoring mistakes early.
- Evolution should be additive so old content does not detonate.
In other words, we wanted the "it worked on my machine" class of bugs to feel lonely.
The roadmap followed thin vertical slices: schema -> validation -> runtime -> tests -> docs. It is less exciting than skipping steps, but dramatically cheaper than emergency debugging after content lock.
We started by making transition behavior explicit:
ease and transition are first-class fields, not implied vibes.
Runtime support landed for named transitions:
cut, crossfade, and directional wipes.
Rust validation and deterministic hashing enforced stable interpretation.
The practical outcome was that two engineers could read the same event and stop arguing about what it "probably" does.
Once transitions existed, timing became the next source of accidental creativity. So we added:
transition_durationtransition_delay
This let transitions run in explicit windows inside event durations. Validation guarded impossible combinations.
Result: fewer magic numbers, fewer accidental instant cuts, fewer post-hoc Slack threads.
Raw event timelines are correct but noisy. Authors wanted "shots," not hand-written repetitive events.
We introduced macro: "shot_sequence":
high-level shot definitions compile into deterministic timeline events.
Compiler errors include source locations, because "line 1, good luck" is not a UX strategy.
This gave us two wins:
- Authoring became faster.
- Runtime stayed simple because it still executes normalized events.
We added keyframe tracks for background and image transform/alpha with per-segment easing.
Interpolation remained deterministic, including edge cases.
This phase mattered because visual polish tends to be where determinism goes to retire. Instead, we made motion math explicit and testable.
If an alpha ramp changed, it changed because someone changed it, not because floating point woke up in a mood.
Writers think in seconds. Engines think in ticks. Both are right; neither should be punished.
We added:
time_secondson eventsticks_per_secondon timelines
Compiler normalization converts seconds to integer ticks with deterministic rules.
When both tick and time_seconds are provided, compatibility checks ensure they resolve identically.
This gave non-engineering authors time-based ergonomics without sacrificing runtime determinism.
Two expansion tracks landed:
- Audio cues
- Ducking
- Fade in/out
- Stingers
- Optional bus targeting
- Post effects
- Letterbox
- Vignette
- Grain
- Grade
- Blur
Both tracks include deterministic hashing and field validation.
Why hash post-fx fields? Because "looks different but nobody touched content" is a great way to lose a sprint.
Cine Linter
We added linting for pacing, overlaps, missing assets, and invalid references. It catches mistakes before runtime preview, which is where mistakes become opinions.
Preview Upgrades
We added operator-facing tools directly in preview UI:
- Curve tools (inspect easing shape/samples)
- Layer inspector (visibility/order/motion state)
- Transition debugger (window, delay, progress)
This converted timeline debugging from "stare at JSON harder" to "observe system state."
Golden-Frame CI Regression
We wired cinematic visual regression into CI:
- Render-mode support for cine bundles in the visual harness
- Dedicated Make targets for cine record/check flows
- CI workflow that runs cutscene-tag checks
- Committed baseline frames for representative branching cutscenes
So now visual regressions get a binary answer in CI, not a committee review in chat.
The durable pattern was:
- Keep runtime primitive and deterministic.
- Put expressive authoring in compile-time macros.
- Validate aggressively at schema boundaries.
- Treat docs and tests as part of the feature, not cleanup.
This preserved flexibility without letting content authoring mutate runtime semantics.
Or in less polite terms: we let authors express intent, but not invent physics.
What This Bought Us
Technically:
- Deterministic playback across CLI/runtime/save-resume
- Backward-compatible schema evolution
- Stable hashing for reproducibility
- CI enforcement of visual expectations
Operationally:
- Faster content iteration
- Earlier error detection
- Fewer "cannot reproduce" incidents
- Better handoff between engineering and content teams
Emotionally:
- Slightly less fear before merges.
The cinematic stack now supports expressive authoring while remaining deterministic, testable, and CI-gated.
The engine still does not understand metaphor. It does, however, reliably render one.