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>
198 lines
7.1 KiB
JavaScript
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;
|