music-video-gen/flow-state/src/scenes/registry.js
Dejvino c099501ead Raise the variety bar to 0.1
Deliberately a target rather than a description of where the library sits. Of
the scenes measured since the region block went in, only Droste Feedback at
0.324 is comfortably over; Plasma Bloom is 0.082, Voronoi Shatter and Apollonian
Gasket 0.062, Moiré Grid 0.047. Most of the library fails this, which is the
point — a bar set where the work already is measures nothing.

Third value in two days, and both previous moves were instrument corrections
rather than changes of mind: the motion block was reading zero for the entire
library, and adding the region block made the total a mean over six blocks
instead of five. Both raised every score, so 0.04 had quietly drifted down
relative to the thing it was judging. Documented as something to re-read off a
fresh gallery whenever the descriptor changes, rather than a number to carry
forward.

The marker now sits at the middle of each meter and the grades land where they
should: 0.047, 0.062 and 0.082 all red, 0.140 amber, 0.324 green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 18:11:33 +02:00

198 lines
7.1 KiB
JavaScript

import { validateModule } from '../params/schema.js';
// STAGES — scenes that draw the song's cast rather than their own content.
// See scenes/stage/README.md and EPIC-3.md.
import { procession } from './stage/procession.js';
import { constellation } from './stage/constellation.js';
import { soloist } from './stage/soloist.js';
import { swarm } from './stage/swarm.js';
import { nebula } from './shader/nebula.js';
import { classicWave } from './shader/classic-wave.js';
import { floatingGeometry } from './shader/floating-geometry.js';
import { synthwaveRun } from './shader/synthwave-run.js';
import { psychedelicDrift } from './shader/psychedelic-drift.js';
import { particleField } from './layers3d/particles.js';
import { horizonLines } from './shader/horizon-lines.js';
import { spectrumSculpture } from './shader/spectrum-sculpture.js';
import { slowOrb } from './shader/slow-orb.js';
import { curlFlow } from './shader/curl-flow.js';
import { plasmaBloom } from './shader/plasma-bloom.js';
import { metaballs } from './shader/metaballs.js';
import { kaleidoTunnel } from './shader/kaleido-tunnel.js';
import { moireGrid } from './shader/moire-grid.js';
import { ridgeTerrain } from './shader/ridge-terrain.js';
import { scanTear } from './shader/scan-tear.js';
import { blockMosh } from './shader/block-mosh.js';
import { neonCity } from './shader/neon-city.js';
import { flora } from './shader/flora.js';
import { fireflyDrift } from './shader/firefly-drift.js';
import { silkRibbon } from './shader/silk-ribbon.js';
import { pylonGrid } from './shader/pylon-grid.js';
import { prismBloom } from './shader/prism-bloom.js';
import { pitchShatter } from './shader/pitch-shatter.js';
import { auroraVeil } from './shader/aurora-veil.js';
import { tideRings } from './shader/tide-rings.js';
import { dustChamber } from './shader/dust-chamber.js';
import { cargoBelt } from './shader/cargo-belt.js';
import { circuitBloom } from './shader/circuit-bloom.js';
import { truchetFold } from './shader/truchet-fold.js';
import { signalDecay } from './shader/signal-decay.js';
import { inkBleed } from './shader/ink-bleed.js';
import { saltFlat } from './shader/salt-flat.js';
import { stormRift } from './shader/storm-rift.js';
import { vortexDrift } from './shader/vortex-drift.js';
import { gateCorridor } from './shader/gate-corridor.js';
import { smokeColumn } from './shader/smoke-column.js';
import { cellDivide } from './shader/cell-divide.js';
import { eclipseField } from './shader/eclipse-field.js';
import { girderLattice } from './shader/girder-lattice.js';
import { quasicrystal } from './shader/quasicrystal.js';
import { timeSmear } from './shader/time-smear.js';
import { rainColumn } from './shader/rain-column.js';
import { magnetLines } from './shader/magnet-lines.js';
import { karmanStreet } from './shader/karman-street.js';
import { turingBloom } from './shader/turing-bloom.js';
import { myceliumWeb } from './shader/mycelium-web.js';
import { scaleMosaic } from './shader/scale-mosaic.js';
import { pendulumTrace } from './shader/pendulum-trace.js';
import { contourMap } from './shader/contour-map.js';
import { shojiGrid } from './shader/shoji-grid.js';
import { balanceStack } from './shader/balance-stack.js';
import { suspensionSpan } from './shader/suspension-span.js';
import { stairwellDescent } from './shader/stairwell-descent.js';
import { aqueductMarch } from './shader/aqueduct-march.js';
import { dataAisle } from './shader/data-aisle.js';
import { voronoiShatter } from './shader/voronoi-shatter.js';
import { apollonianGasket } from './shader/apollonian-gasket.js';
import { isometricBlocks } from './shader/isometric-blocks.js';
import { halftoneMisprint } from './shader/halftone-misprint.js';
import { drosteFeedback } from './shader/droste-feedback.js';
import { analogWow } from './shader/analog-wow.js';
import { mountainFlight } from './shader/mountain-flight.js';
/**
* The scene library. Families exist so the arc driver can choose by section
* character rather than at random — a breakdown never lands on a strobing glitch
* scene, and an intro never opens at full density.
*/
export const FAMILIES = {
flow: { label: 'Flow', energy: [0.0, 0.7] },
organic: { label: 'Organic', energy: [0.0, 0.8] },
minimal: { label: 'Minimal', energy: [0.0, 0.45] },
structural: { label: 'Structural', energy: [0.3, 0.9] },
geometric: { label: 'Geometric', energy: [0.4, 1.0] },
glitch: { label: 'Glitch', energy: [0.6, 1.0] },
};
const MODULES = [
nebula,
classicWave,
floatingGeometry,
synthwaveRun,
psychedelicDrift,
particleField,
// Phase 7 additions. 'minimal' came first: with the family empty, intros and
// breakdowns fell through to flow/organic and every track opened at density.
horizonLines,
spectrumSculpture,
slowOrb,
curlFlow,
plasmaBloom,
metaballs,
kaleidoTunnel,
moireGrid,
ridgeTerrain,
scanTear,
blockMosh,
neonCity,
flora,
fireflyDrift,
silkRibbon,
pylonGrid,
prismBloom,
pitchShatter,
// Ten added to widen the library past the point where a track's rosters
// start repeating: the casting rule shrinks the pool per track, so depth in
// every family is what keeps two videos from drawing the same four scenes.
// Weighted toward the 'space' and 'shape' traits, which were the thinnest
// and therefore the signatures most likely to run out of cast.
auroraVeil,
tideRings,
dustChamber,
cargoBelt,
circuitBloom,
truchetFold,
signalDecay,
inkBleed,
saltFlat,
stormRift,
vortexDrift,
gateCorridor,
smokeColumn,
cellDivide,
eclipseField,
girderLattice,
quasicrystal,
timeSmear,
rainColumn,
magnetLines,
karmanStreet,
turingBloom,
myceliumWeb,
scaleMosaic,
pendulumTrace,
contourMap,
shojiGrid,
balanceStack,
suspensionSpan,
stairwellDescent,
aqueductMarch,
dataAisle,
voronoiShatter,
apollonianGasket,
isometricBlocks,
halftoneMisprint,
drosteFeedback,
analogWow,
// Stages. Registered alongside the scenes so every existing gate covers
// them; what makes them different is `consumes`, not where they live.
procession,
constellation,
soloist,
swarm,
mountainFlight,
];
const errors = [];
for (const m of MODULES) {
const e = validateModule(m);
if (e.length) errors.push(...e);
if (m.family && !FAMILIES[m.family]) errors.push(`${m.name}: unknown family '${m.family}'`);
}
if (errors.length) {
// Fail loudly at import: a malformed scene must never reach the compositor,
// where the symptom would be a black frame with no explanation.
console.error('[registry] invalid scene modules:\n' + errors.join('\n'));
}
export const scenes = MODULES;
export const sceneErrors = errors;
export function sceneByName(name) {
return MODULES.find((m) => m.name === name) || null;
}
export function scenesInFamily(family) {
return MODULES.filter((m) => m.family === family);
}
export function familyNames() {
return Object.keys(FAMILIES);
}
export default scenes;