When a Deterministic Playlist Wasn't

Published:

← Back to Blog


A rendering problem does not always live where it appears. After improving the mesh, camera, texture sampling, and material of an eight-turn NearRing, one six-second interval still looked nearly dead. From about 0:51 through 0:56 the object became a quiet gray ring, even though the soundtrack remained loud.

The rendered surface was only the final witness. The real failure happened earlier, where two independent systems believed they controlled the same visual state.

TWO COMPETING SCHEDULERS
NearRing at 53 seconds with a nearly uniform gray material
PLAYLIST IS AUTHORITATIVE
NearRing at 53 seconds with vivid multicolored audio patterns
Matching frames at 0:53 from the same audio and CSV playlist. GPU geometry, camera, and material are unchanged between these two stages.

Push the Investigation Upstream

Audio measurements ruled out silence: one-second windows in the reported interval stayed around -10 to -14 dB RMS. We then decoded the first minute to raw 48 kHz stereo PCM and replayed the existing video/tracks/Aces High/playlist.csv through the 160×100 CPU renderer. This removed the 3D mesh, shader, depth buffer, and camera from the test.

Frames 0:51 through 0:56 each expanded to exactly one palette color and had zero image entropy. The gray ring was therefore an accurate rendering of an inactive indexed buffer, not a projection or lighting illusion.

A Real Projection Bug, but Not the Whole Answer

The active CSV row selected Wireframe3D. Its virtual camera sat only 0.6 × width from a ribbon spanning ±0.5 × width. At the nearest rotation angle the perspective denominator fell to 0.1 × width, allowing 8× magnification. A loud waveform could be projected entirely outside the simulation buffer.

The revised projection places the camera one full buffer width away, uses a focal length of 0.75 × width, and scales waveform height from the buffer height:

let focal = W as f32 * 0.75;
let depth_offset = W as f32;
let amp_scale =
    (H as f32 * 0.25 / 128.0)
    / (1 << scale.min(3)) as f32;

The maximum magnification is now 1.5×, so even a full-range sample remains inside the frame. A regression test renders all 316 sampled phases of one complete rotation and requires visible pixels at every phase.

Yet replaying the exact music still produced the six dead seconds. That negative result was decisive: another state change had removed the wave renderer itself.

One Scene, Two Schedulers

A video playlist is already a scheduler. Each CSV row defines a base configuration, a duration, and a state count. At each transition the playlist either advances to the next row or deterministically mutates one visual parameter.

The core engine also has an auto-changer for interactive use. Beats, elapsed time, and a boring-scene detector can trigger randomize_one or randomize_all. The headless renderer loaded and advanced the CSV but left this second scheduler enabled. It could silently replace the selected wave, flame, palette, or display between authored transitions.

The fix makes ownership explicit:

// The playlist owns deterministic mutations.
engine.locked = playlist.is_some();

Here locked disables only the independent engine auto-changer. The playlist still advances, applies base configurations, and performs its requested mutations. Interactive rendering without a playlist retains the normal beat, timer, and boredom behavior.

Verify the Interval, Not Just the Fix

The exact CPU replay changed every sampled frame from 0:51 through 0:56 from one color and zero entropy to 231–238 distinct colors and entropy between 0.476 and 0.634. One-second samples across 0:49–0:59 then showed moving multicolored structure on the GPU surface throughout the previously dead interval.

Finally, the existing playlist was preserved and the full video was regenerated through the normal command:

nix develop --command cargo xtask video \
  "Aces High" --reuse-playlist

This bug is a useful architecture warning. Determinism is not merely a fixed random seed; it also requires one clear owner for every state transition. A test can prove that an individual renderer never vanishes, while an interval replay proves that the orchestration layer continues to select that renderer in the first place.