Comfort: agitation ceiling, composition validator and whole-screen caps
Whole-screen flashing/coherent motion is now a gated comfort signal rather than a hunt-and-tweak. Three-arm agitation (flash/3, tile-motion/0.32, structure/0.12) over a 1s floating window, worst window wins — engine/agitation.js. Policy centralised in look/comfort.js: director comfort appetite, per-scene agitationOf proxy, agitationWeight lean and stackAgitation. Canvas beat/flux capped in Layer.resolveParams (0.15/0.25, spike→smooth) so one contract change tames every full-frame scene; lint warns above the cap. Selection now filters before casting (look/scene-filter.js, 12+ ranged metrics from checks/scene-metrics.js, SCHEMA 7) and validates after stacking (look/composition-validator.js) with a deterministic retune: drop overlay → swap quiet composable → reselect filtered shot. Fix validator attempt-3 ground-layer bug that crashed ?song=dusk (LookGenerator now passes module, not layer). Eight canvas shaders window their beat/flux (focusField + per-seed hash mask) and report the scaled amount to Layer so the cap stays authoritative: scan-tear, gate-corridor, analog-wow, block-mosh, metaballs, pitch-shatter, scale-mosaic, tide-rings. Checks: scene-gate comfort gate (agitation ≤ 0.70), filmstrip/gallery meters + per-scene drawer, metadata SCHEMA 7 with ranged metrics and fingerprint over agitation/comfort sources. main.js surfaces COMPOSITION_REFUSED as a user-facing error. Lint: 114 files clean, 71 scenes ok. All 17 songs generate looks. Build passes. metadata.json refresh deferred to gallery → Refresh metadata (SCHEMA 7 writer in vite.config). Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -378,6 +378,50 @@ console.log('\nscene schema lint');
|
||||
}
|
||||
}
|
||||
|
||||
// A — whole-screen comfort: canvas scenes (≥50% coverage) that swerve
|
||||
// on the beat drive the ENTIRE view on every kick (≈2 Hz at 124 BPM).
|
||||
// Those are capped centrally in engine/Layer.js (CANVAS_BEAT_CAP=0.15
|
||||
// + spike→smooth, FLUX 0.25) so one change tames every canvas. This lint
|
||||
// is the feedback: a declaration above the cap still builds (clamped at
|
||||
// runtime) but is warned here so the author can lower it or localise the
|
||||
// response to a region. Staleness via checks/metadata.js.
|
||||
for (const [pname, pr] of Object.entries(module.reactive || {})) {
|
||||
const feat = pr.feature;
|
||||
const amt = typeof pr.amount === 'number' ? pr.amount : 0;
|
||||
if (feat === 'beat' && amt > 0.15) {
|
||||
const cov = (() => {
|
||||
try {
|
||||
const m = JSON.parse(readFileSync(join(root, 'src/scenes/metadata.json'), 'utf8'));
|
||||
const row = m?.scenes?.[module.name];
|
||||
return typeof row?.coverage === 'number' ? row.coverage : null;
|
||||
} catch { return null; }
|
||||
})();
|
||||
const isCanvas = cov !== null ? cov >= 0.5 : false;
|
||||
if (isCanvas) {
|
||||
// Warning, not fail — A works via runtime clamp; the
|
||||
// declaration should be lowered in B when the effect is
|
||||
// localised to a region. Keep lint green while the clamp is
|
||||
// the back-stop.
|
||||
console.warn(` ⚠ ${id}: reactive '${pname}' beat ${amt.toFixed(2)} on canvas (cov ${(cov*100).toFixed(0)}%) — ` +
|
||||
`whole-screen jumps; runtime capped to 0.15 (lower or localise the response)`);
|
||||
}
|
||||
}
|
||||
if (feat === 'flux' && amt > 0.25) {
|
||||
const cov = (() => {
|
||||
try {
|
||||
const m = JSON.parse(readFileSync(join(root, 'src/scenes/metadata.json'), 'utf8'));
|
||||
const row = m?.scenes?.[module.name];
|
||||
return typeof row?.coverage === 'number' ? row.coverage : null;
|
||||
} catch { return null; }
|
||||
})();
|
||||
const isCanvas = cov !== null ? cov >= 0.5 : false;
|
||||
if (isCanvas) {
|
||||
console.warn(` ⚠ ${id}: reactive '${pname}' flux ${amt.toFixed(2)} on canvas (cov ${(cov*100).toFixed(0)}%) — ` +
|
||||
`runtime capped to 0.25`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rate params: anything the shader multiplies absolute time by must be
|
||||
// flagged `rate: true`, which stops reactivity and drift from touching it.
|
||||
// Modulating such a param jumps the phase by elapsed*delta — sixty seconds
|
||||
|
||||
Reference in New Issue
Block a user