Side quest 2: give three churning scenes something to develop
Curl Flow, Signal Decay and Circuit Bloom had no parameter that changed their structure. Each was one process at one scale, statistically identical everywhere and at every moment — which is the technical statement of "it looks the same for five minutes". Every pixel moving, the image never changing. Each got the large-scale structure it was missing, and the slow axis now has something real to walk: - Curl Flow gains a CURRENT: one broad band carries the filaments and the rest runs bare. The axis is the band's strength rather than its position, and that choice was measured — moving the band preserves the frame's total energy, so a ten-second average of it is nearly the same image wherever it sits (0.0028 across a full sweep). Opening and closing it changes how much of the frame is lit at all. The current gates the final image rather than only the vein term, because the feedback trail and the flow tint each fill the frame on their own. - Signal Decay gains a DAMAGE FRONT: lock falls away on one side of a moving boundary, so the stack has a shape instead of every lane being an independent coin flip. Hiss had to be gated by it too — hiss rises as lock falls, so without that a damaged lane simply traded signal for noise and carried the same energy, and the front cancelled itself out. - Circuit Bloom now actually BLOOMS. `reach` was a fixed vignette, so the packets ran, the pads blinked and the board never changed. Its range stops at 1.6 because the frame's far corner is ~1.4 units out and travel spent past that changes nothing — the first attempt wasted most of the axis up there. The gate itself was wrong, and this is the more important half of the commit. It compared the scene at thirty seconds against the scene at two and a half minutes and divided by what it did between those points with frozen parameters. Those are two different places in the song, so the denominator was full of audio reactivity: it was really asking "does the axis move this scene MORE than the music does", which no scene should have to pass. Circuit Bloom failed at 0.92x while its mean luminance moved 5.6x across the axis — a large structural change scored as nothing. The experiment now holds time constant and varies only the axis, against a control that is the same parameters over the next window — the residual churn the ten-second average failed to cancel, which is the noise floor it has to beat. With the axis disabled the two parameter sets are identical and the ratio is 0.00 by construction, so the companion check is a real discriminator rather than a formality. It reads 0.00x on all six scenes. Moiré Grid 28.7x · Gate Corridor 14.3x · Truchet Fold 3.3x · Signal Decay 1.9x · Curl Flow 1.7x · Circuit Bloom 1.4x, against a 1.25x bar. 104/104 checks pass including the slow set. Closes SIDE-QUESTS.md §2 and the open half of EPIC-2.md §3.4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -556,7 +556,29 @@ function meanAbs(a, b) {
|
||||
return sum / a.length / 255;
|
||||
}
|
||||
|
||||
/** How much the declared axis moves a scene, against how much it moves anyway. */
|
||||
/**
|
||||
* How much the declared axis moves a scene, against the metric's own noise.
|
||||
*
|
||||
* The experiment holds TIME constant and varies only the axis, which the first
|
||||
* version of this check did not — and getting that wrong made it measure
|
||||
* something else entirely.
|
||||
*
|
||||
* That version compared the scene at thirty seconds against the scene at two
|
||||
* and a half minutes, and divided by what the scene did between those points
|
||||
* with its parameters frozen. But those are two different places in the song,
|
||||
* so the denominator was full of audio reactivity: the check was really asking
|
||||
* "does the slow axis move this scene MORE than the music does", which is a
|
||||
* different and much harder question, and one no scene should have to pass.
|
||||
* Circuit Bloom failed it at 0.92x while its mean luminance was moving 5.6x
|
||||
* across the axis — a large structural change, scored as nothing.
|
||||
*
|
||||
* So: one frame window, two parameter sets, and a control that is the same
|
||||
* parameters over the next window — the residual churn the ten-second average
|
||||
* failed to cancel, which is exactly the noise floor this measurement has to
|
||||
* beat. With the axis disabled the two parameter sets are identical and the
|
||||
* ratio is 0.00 by construction, which is what makes the companion check below
|
||||
* a real discriminator rather than a formality.
|
||||
*/
|
||||
function axisRatio(engine, arc, look, track, module, { withAxis }) {
|
||||
const paramsAt = (seconds) => {
|
||||
const out = sampleValues(module, new Rng(77), look.sections[0].bias,
|
||||
@@ -570,18 +592,16 @@ function axisRatio(engine, arc, look, track, module, { withAxis }) {
|
||||
}
|
||||
return out;
|
||||
};
|
||||
// The control is the same scene with its parameters held: whatever it does
|
||||
// on its own between these two points in the track.
|
||||
const frozenA = averagedFrame(engine, look, module,
|
||||
sampleValues(module, new Rng(77), look.sections[0].bias, look.personality.temperament), 1800);
|
||||
const frozenB = averagedFrame(engine, look, module,
|
||||
sampleValues(module, new Rng(77), look.sections[0].bias, look.personality.temperament), 9000);
|
||||
const movedA = averagedFrame(engine, look, module, paramsAt(30), 1800);
|
||||
const movedB = averagedFrame(engine, look, module, paramsAt(150), 9000);
|
||||
|
||||
const own = meanAbs(frozenA, frozenB);
|
||||
const moved = meanAbs(movedA, movedB);
|
||||
return { own, moved, ratio: moved / Math.max(1e-6, own) };
|
||||
const FRAME = 1800;
|
||||
const WINDOW = 90 * 7;
|
||||
const early = averagedFrame(engine, look, module, paramsAt(0), FRAME);
|
||||
const late = averagedFrame(engine, look, module, paramsAt(track.duration), FRAME);
|
||||
const control = averagedFrame(engine, look, module, paramsAt(0), FRAME + WINDOW);
|
||||
|
||||
const moved = meanAbs(early, late);
|
||||
const noise = meanAbs(early, control);
|
||||
return { moved, noise, ratio: moved / Math.max(1e-6, noise) };
|
||||
}
|
||||
|
||||
check(11, 'a declared slow axis actually changes the scene', () => {
|
||||
@@ -604,8 +624,8 @@ check(11, 'a declared slow axis actually changes the scene', () => {
|
||||
const r = axisRatio(engine, arc, look, t, module, { withAxis: true });
|
||||
detail.push(`${module.name} ${r.ratio.toFixed(2)}x`);
|
||||
if (r.ratio < 1.25) {
|
||||
problems.push(`${module.name}: axis moved ${r.moved.toFixed(4)} against ` +
|
||||
`${r.own.toFixed(4)} on its own — only ${r.ratio.toFixed(2)}x`);
|
||||
problems.push(`${module.name}: axis moved ${r.moved.toFixed(4)} against a ` +
|
||||
`${r.noise.toFixed(4)} noise floor — only ${r.ratio.toFixed(2)}x`);
|
||||
}
|
||||
}
|
||||
if (!declared.length) problems.push('no scene declares a slow axis');
|
||||
|
||||
Reference in New Issue
Block a user