Phase 4: arc driver ("C" brain)

Three timescales now stack: per-frame reactivity, per-section seeded LFO
drift, and whole-song scene changes with lookahead. Layer instances are
cached per section and reused across crossfades — rebuilding them per frame
would recompile shaders every transition.

Crossfades run forward from a boundary: the outgoing scene holds while the
incoming one fades in over it.

Three real bugs, each found by a check that had to be rewritten first:

1. A pop exactly at every transition. buildSlope is discontinuous by
   construction (~1 before a boundary, 0 after), and the outgoing layer is
   still on screen when it flips — collapsing its lookahead ramp in one
   frame. It now holds the slope it had entering the boundary.

2. FeatureTrack.at() returns a REUSED row object, and _boundarySlope()
   called at() again mid-render, rewriting the features the layer was about
   to read. Symptom: a frame correct on every repeat and wrong the first
   time — invisible to fresh-vs-fresh comparison, and wrong in every export,
   since export renders each frame exactly once. Now indexes the typed array
   directly, with the aliasing hazard documented on at(), and a new check
   covers the whole bug class.

3. Warm-up converged to 1%, leaving a visible 0.015 difference at heavy
   feedback settings. Now targets 0.1%.

Two checks were themselves wrong and were rebuilt: a raw delta threshold
and an outlier-vs-local-median test both flag beat flashes as pops, and a
control window taken from a different scene reads an ordinary busy scene as
a 9x spike. The working formulation A/Bs each boundary against the interior
of the two scenes adjacent to it.

PLAN.md §6 corrected: boundary seeks are NOT exact for free. Layer state is
re-seeded there but the feedback buffer is global and carries across.
Clearing it at boundaries would buy exactness for a visible flash at every
transition; warm-up is the better trade and applies everywhere.

Gate 9/9.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dejvino
2026-08-05 11:30:50 +02:00
co-authored by Claude Opus 5
parent 022c267888
commit 5f25437b89
7 changed files with 812 additions and 13 deletions
+17 -8
View File
@@ -266,15 +266,24 @@ Stateful layers — feedback buffers, particle systems — mean frame *N* depend
before it. Scrubbing to an arbitrary frame therefore can't be exact for free. Three-part
resolution:
1. **Warm-up.** On seek, render *K* frames (default ≈120, about 2s) as fast as possible
off-screen before displaying. Converges feedback state to visually correct.
2. **Section-boundary seeks are exact.** Particle systems and layer state are re-seeded
deterministically at each section boundary, so jumping to a boundary — the common review
action — needs no warm-up and is frame-exact.
3. **Export is always sequential**, so it is exact everywhere by construction.
1. **Warm-up.** On seek, render *K* frames off-screen before displaying. *K* is computed
from the feedback decay rather than fixed — the residual after *n* frames is `decay^n`,
so `log(0.001)/log(decay)` frames converges to a tenth of a percent (96 frames at
decay 0.93). A light-feedback look seeks almost instantly; a heavy one still lands.
2. **Export is always sequential**, so it is exact everywhere by construction.
Documented consequence: scrubbing to a random mid-section frame shows a *converged*, not
bit-exact, image. Reviewing a transition never hits this, because transitions are boundaries.
> **Correction, made during Phase 4.** The original plan claimed section-boundary seeks
> would be frame-exact with no warm-up, because layer state is re-seeded there. That is
> wrong: layer state is only half the story, and the compositor's feedback buffer is
> *global* — it carries straight across a boundary like any other frame. Making boundary
> seeks exact would mean clearing feedback at every transition, which trades a cheap
> warm-up for a visible flash on every scene change. Warm-up is the better trade, and it
> applies everywhere rather than only mid-section. Measured: converges to a 0.00000 mean
> difference. With feedback disabled, seeks are bit-exact anywhere, which is what proves
> nothing *else* in the pipeline is carrying state.
Documented consequence: scrubbing shows a *converged*, not bit-exact, image whenever
feedback is enabled — which is visually indistinguishable, and exact once feedback is off.
---