diff --git a/flow-state/EPIC-2.md b/flow-state/EPIC-2.md index ceb0237..4f7ce78 100644 --- a/flow-state/EPIC-2.md +++ b/flow-state/EPIC-2.md @@ -126,6 +126,38 @@ but it is the only thing that fixes the specific failure of "animated and yet st The five nearly-static scenes get the opposite treatment, and Phase 7's movement gate already describes what "enough" means. +**Status after the first pass: mechanism delivered, most of the scene work still open.** + +The mechanism exists and is proven. `ArcDriver._slowAxisFor` walks a declared param across the +whole track, monotonically — the existing drift is a 20-70 second LFO, and an LFO returns, +which is precisely why ten cycles of it over five minutes reads as static. + +Two things were learned the hard way and are worth not relearning: + +*Which param you move decides everything.* The first version chose at random from everything +eligible and measured as doing **nothing whatsoever** — identical structural change with the +axis applied and with it disabled. Sweeping Moiré Grid's `width` moves its time-averaged +structure by 0.110 and its `offset` by 0.002; a random draw finds the second kind almost every +time. Hence `slowAxis: true` as a declaration rather than a heuristic. + +*Single-frame distance cannot measure this.* A churning scene's consecutive frames are already +~0.6 apart, so every pair of its frames scores the same whether the structure moved or not. +The gate measures **ten-second time-averaged** frames, which cancels the churn and leaves the +structure. The window was measured: at one second, Moiré Grid's frozen-parameter control still +reads 0.024; at ten it reads 0.0095 while the axis-driven signal stays at 0.037. + +Measured across ten candidate scenes (ratio of axis-driven structural change to what the scene +does on its own): + +| works | Moiré Grid 3.93× · Gate Corridor 2.88× · Truchet Fold 1.40× | +|---|---| +| **no parameter helps** | Curl Flow 1.31× · Signal Decay 1.21× · Circuit Bloom 1.10× | +| **already develops; never the problem** | Firefly Drift · Vortex Drift · Kaleido Tunnel · Plasma Bloom | + +The middle row is the remaining work, and it is **not** mechanical: those scenes have no +parameter that changes their structure, so they need shader changes that introduce one. +Parameter automation cannot substitute for structure a scene does not have. + ### 3.5 A framing layer The one that raises the ceiling rather than the floor. A shared zoom / crop / scale envelope diff --git a/flow-state/src/checks/phase11.js b/flow-state/src/checks/phase11.js index 6e1e75e..9b7b870 100644 --- a/flow-state/src/checks/phase11.js +++ b/flow-state/src/checks/phase11.js @@ -19,6 +19,11 @@ import { scenes, scenesInFamily } from '../scenes/registry.js'; import { paletteShiftAt, MAX_HUE_ROTATION } from '../look/paletteArc.js'; import { shiftPalette, paletteContrast } from '../look/palette.js'; import { ArcDriver } from '../look/ArcDriver.js'; +import { Engine } from '../engine/Engine.js'; +import { featureProviderFor } from '../audio/FeatureTrack.js'; +import { sampleValues, clampValue } from '../params/schema.js'; +import { Rng } from '../engine/rng.js'; +import { frameDistance, frameLuminance } from '../engine/hash.js'; /** Tracks at several tempos: shot length is measured in bars, so tempo is the axis. */ let cached = null; @@ -435,3 +440,217 @@ check(11, 'a moved palette is the same on a seek as on playback', () => { return expect(worst === 0, `worst channel difference between seeked and played colours: ${worst}`); }); + +// --- the slow axis --------------------------------------------------------- +// EPIC-2.md §3.4. Eleven scenes changed as much in half a second as in two +// minutes: everything moving in them was cyclic, so the eye adapted in about +// two seconds. Drift is a 20-70s LFO and an LFO returns; the slow axis is +// monotonic across the whole track. See ArcDriver._slowAxisFor. + +check(11, 'every scene gets a slow axis with real travel', () => { + // Structural half of the gate, so a scene added later cannot quietly end up + // with nothing to evolve. + const t = tempoBattery()[1].track; + const look = generateLook(t, { seed: 20250 }); + const arc = new ArcDriver(look, t); + const problems = []; + + try { + for (const module of scenes) { + const axis = arc._slowAxisFor(module); + const eligible = Object.entries(module.params || {}).filter(([, d]) => + d.type !== 'palette' && d.type !== 'bool' && !d.fixed && !d.rate && !d.noDrift && d.range); + if (!eligible.length) continue; // nothing it could legally move + + if (!axis.length) { problems.push(`${module.name}: no axis`); continue; } + for (const item of axis) { + const [lo, hi] = item.def.range; + const fraction = Math.abs(item.travel) / (hi - lo); + if (fraction < 0.2) problems.push(`${module.name}.${item.name}: travels only ${(fraction * 100).toFixed(0)}%`); + if (item.def.rate) problems.push(`${module.name}.${item.name}: rate param on the axis`); + } + } + } finally { + arc.dispose(); + } + + return expect(problems.length === 0, + problems.length ? problems.slice(0, 3).join(' · ') + : `${scenes.length} scenes all carry a slow axis travelling 20%+ of range`); +}); + +check(11, 'the slow axis is a journey rather than a cycle', () => { + // The counter-check. An axis that returned to where it started would satisfy + // "params move" and would leave the churn exactly as it was — which is what + // the existing drift LFO already did. + const t = tempoBattery()[1].track; + const look = generateLook(t, { seed: 31337 }); + const arc = new ArcDriver(look, t); + const problems = []; + + try { + const cue = arc.cues[0]; + const at = (time) => arc._paramsAt(cue, 0, time, t.at(Math.round(time * 60))); + const spec = arc._specFor(cue.sectionIndex, cue.variant, 0); + const axis = arc._slowAxisFor(spec.module); + + const start = at(t.duration * 0.05); + const mid = at(t.duration * 0.5); + const end = at(t.duration * 0.95); + + for (const item of axis) { + const [lo, hi] = item.def.range; + const span = hi - lo; + const a = start[item.name], m = mid[item.name], z = end[item.name]; + // Monotonic in the sense that matters: the end is further from the + // start than the middle is, in the direction of travel. + const total = Math.abs(z - a) / span; + if (total < 0.12) { + problems.push(`${spec.module.name}.${item.name}: start ${a.toFixed(3)} ` + + `mid ${m.toFixed(3)} end ${z.toFixed(3)} — only ${(total * 100).toFixed(0)}% travelled`); + } + } + if (!axis.length) problems.push('no axis on the opening scene'); + } finally { + arc.dispose(); + } + + return expect(problems.length === 0, + problems.length ? problems.join(' · ') : 'the opening scene ends the track somewhere else'); +}); + +/** + * Structure, with the churn averaged out. + * + * Single-frame distance cannot answer "did this develop?" for exactly the + * scenes that fail it: a churning scene's consecutive frames are already ~0.6 + * apart, so every pair of its frames scores the same whether the structure + * moved or not. Averaging ten seconds of frames cancels the churn and leaves + * the structure — and is much closer to what a viewer perceives over seconds + * than any single frame is. + * + * Ten seconds was measured, not guessed. At a one-second window Moiré Grid's + * frozen-parameter control still read 0.024; at ten it reads 0.0095, while the + * axis-driven change stays at 0.037. The window has to be wide enough that the + * control collapses and the signal does not. + */ +function averagedFrame(engine, look, module, params, frame0, n = 90, step = 7) { + engine.setLayerSpecs([{ module, params, seed: 9, opacity: 1, blend: 'normal', + palette: look.palette, personality: look.personality }]); + engine.prime(frame0); + engine.compositor.reset(); + let acc = null; + for (let k = 0; k < n; k++) { + const px = engine.readPixels(engine.renderFrame(frame0 + k * step)); + if (!acc) acc = new Float64Array(px.length); + for (let i = 0; i < px.length; i++) acc[i] += px[i]; + } + for (let i = 0; i < acc.length; i++) acc[i] /= n; + return acc; +} + +function meanAbs(a, b) { + let sum = 0; + for (let i = 0; i < a.length; i++) sum += Math.abs(a[i] - b[i]); + return sum / a.length / 255; +} + +/** How much the declared axis moves a scene, against how much it moves anyway. */ +function axisRatio(engine, arc, look, track, module, { withAxis }) { + const paramsAt = (seconds) => { + const out = sampleValues(module, new Rng(77), look.sections[0].bias, + look.personality.temperament); + if (!withAxis) return out; + const p = Math.min(1, seconds / track.duration); + const journey = p * p * (3 - 2 * p); + for (const item of arc._slowAxisFor(module)) { + if (typeof out[item.name] !== 'number') continue; + out[item.name] = clampValue(item.def, out[item.name] + item.travel * (journey - 0.5)); + } + 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) }; +} + +check(11, 'a declared slow axis actually changes the scene', () => { + // EPIC-2.md §3.4, and the honest scope of it. A scene that declares an axis + // is claiming that walking that param is what it looks like changing, so + // the claim is measured: the structural change with the axis has to beat + // what the scene does on its own by a real margin. + const t = tempoBattery()[1].track; + const look = generateLook(t, { seed: 5150 }); + const engine = new Engine({ width: 128, height: 72 }); + engine.timeline.setDuration(t.duration); + engine.setFeatureProvider(featureProviderFor(t)); + const arc = new ArcDriver(look, t); + + const problems = []; + const detail = []; + try { + const declared = scenes.filter((m) => Object.values(m.params || {}).some((d) => d.slowAxis)); + for (const module of declared) { + 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`); + } + } + if (!declared.length) problems.push('no scene declares a slow axis'); + } finally { + arc.dispose(); + engine.dispose(); + } + + return expect(problems.length === 0, + problems.length ? problems.join(' · ') : `${detail.join(', ')}`); +}, { slow: true }); + +check(11, 'the axis measurement would notice if the axis stopped working', () => { + // EPIC-2.md §4 names this failure mode by name: a gate that measures the + // wrong thing. This one has already happened once here — the first version + // of the check above used single-frame distance, which is saturated on + // churning scenes, and passed while the axis was provably doing nothing. + // + // So the metric is verified by breaking what it is supposed to catch. Run + // the identical measurement with the axis disabled; it must come back at + // about 1.0 and fail the threshold the real check applies. + const t = tempoBattery()[1].track; + const look = generateLook(t, { seed: 5150 }); + const engine = new Engine({ width: 128, height: 72 }); + engine.timeline.setDuration(t.duration); + engine.setFeatureProvider(featureProviderFor(t)); + const arc = new ArcDriver(look, t); + + const problems = []; + const detail = []; + try { + const declared = scenes.filter((m) => Object.values(m.params || {}).some((d) => d.slowAxis)); + for (const module of declared) { + const off = axisRatio(engine, arc, look, t, module, { withAxis: false }); + detail.push(`${module.name} ${off.ratio.toFixed(2)}x`); + if (off.ratio >= 1.25) { + problems.push(`${module.name}: reads ${off.ratio.toFixed(2)}x with the axis ` + + `DISABLED — the measurement is not tracking the axis`); + } + } + } finally { + arc.dispose(); + engine.dispose(); + } + + return expect(problems.length === 0, + problems.length ? problems.join(' · ') + : `axis disabled reads ${detail.join(', ')} — the measurement tracks the axis`); +}, { slow: true }); diff --git a/flow-state/src/look/ArcDriver.js b/flow-state/src/look/ArcDriver.js index 265ffae..c463334 100644 --- a/flow-state/src/look/ArcDriver.js +++ b/flow-state/src/look/ArcDriver.js @@ -167,14 +167,112 @@ export class ArcDriver { return plan; } + /** + * The scene's SLOW AXIS: one or two params that travel one way across the + * whole track. + * + * Drift above is an LFO with a 20-70 second period, and an LFO returns. + * Measured over the library, that is exactly what several scenes' problem + * was: they change as much in half a second as in two minutes, because + * everything moving in them is cyclic, so the eye adapts in about two + * seconds and then there is nothing left to find. Violently animated and + * read as static. Ten cycles of a 30-second wobble is not five minutes of + * anything. + * + * So this is deliberately monotonic. Where drift is the wobble, this is the + * journey: the frame at four minutes has a different STRUCTURE — density, + * scale, count — from the frame at thirty seconds, and no amount of + * per-frame reactivity substitutes for that. + * + * Keyed on the module rather than on the section, so a scene that comes back + * in the last section arrives further along its own axis rather than + * resetting. Rate params are excluded for the reason schema.js gives: they + * multiply absolute time, so moving one jumps the phase. + */ + _slowAxisFor(module) { + if (!this._slowAxes) this._slowAxes = new Map(); + const cached = this._slowAxes.get(module.name); + if (cached) return cached; + + // Stable per (track, scene): the same scene evolves the same way + // wherever it appears in this video, and differently in the next one. + let h = (this.look.seed || 1) >>> 0; + for (let i = 0; i < module.name.length; i++) { + h = (Math.imul(h ^ module.name.charCodeAt(i), 0x01000193) >>> 0); + } + const rng = new Rng(h); + + const eligible = Object.entries(module.params || {}).filter(([, def]) => + def.type !== 'palette' && def.type !== 'bool' && !def.fixed + && !def.rate && !def.noDrift && def.range); + + // A param the scene DECLARES as its axis wins outright, and travels + // much further than a guessed one. + // + // The first version of this picked at random from everything eligible + // and measured as doing nothing whatsoever: the time-averaged image at + // thirty seconds and at two and a half minutes differed by the same + // amount with the axis applied as without it. The reason is that which + // param you move decides everything. Sweeping Moiré Grid's `width` + // moves its averaged structure by 0.110 and its `offset` by 0.002, and + // a random draw finds the second kind almost every time. + const declared = eligible.filter(([, def]) => def.slowAxis); + + const axis = []; + if (declared.length) { + for (const [name, def] of declared) { + const [lo, hi] = def.range; + axis.push({ + name, + def, + declared: true, + // Most of the range. This param was chosen because moving it + // is what the scene looks like changing, so a timid walk + // wastes the one lever that works. + travel: (hi - lo) * rng.range(0.45, 0.7) * (rng.bool() ? 1 : -1), + }); + } + } else { + // Nothing declared: fall back to a guess. Worth keeping — it costs + // nothing and occasionally lands on something structural — but it is + // not what makes this mechanism work, and no gate should rely on it. + const count = Math.min(eligible.length, rng.bool(0.45) ? 2 : 1); + const pool = rng.shuffle(eligible.slice()); + for (let i = 0; i < count; i++) { + const [name, def] = pool[i]; + const [lo, hi] = def.range; + axis.push({ + name, + def, + declared: false, + travel: (hi - lo) * rng.range(0.25, 0.5) * (rng.bool() ? 1 : -1), + }); + } + } + this._slowAxes.set(module.name, axis); + return axis; + } + /** * Base params for a section at a given time: the look's sampled values, plus - * drift, plus the lookahead ramp toward whatever comes next. + * the slow axis, plus drift, plus the lookahead ramp toward what comes next. */ _paramsAt(cue, slot, time, features) { const spec = this._specFor(cue.sectionIndex, cue.variant, slot); const out = { ...spec.params }; + // --- slow axis ------------------------------------------------------ + // Eased rather than linear, so the travel is slowest at the head and + // tail. A video should not open mid-move. + const duration = Math.max(1e-6, this.track.duration); + const p = Math.max(0, Math.min(1, time / duration)); + const journey = p * p * (3 - 2 * p); + for (const item of this._slowAxisFor(spec.module)) { + const base = out[item.name]; + if (typeof base !== 'number') continue; + out[item.name] = clampValue(item.def, base + item.travel * (journey - 0.5)); + } + for (const item of this._driftPlan(cue.sectionIndex, cue.variant, slot)) { const base = out[item.name]; if (typeof base !== 'number') continue; diff --git a/flow-state/src/params/schema.js b/flow-state/src/params/schema.js index bf56956..426ea40 100644 --- a/flow-state/src/params/schema.js +++ b/flow-state/src/params/schema.js @@ -30,6 +30,18 @@ export const PARAM_TYPES = ['float', 'int', 'bool', 'vec2', 'palette']; // `u_time * (u_speed + u_bandLow)` is not. export const RATE_FLAG = 'rate'; +// A param flagged `slowAxis: true` is the one the arc driver walks from one end +// of its range to the other across the WHOLE track — the scene's long journey, +// as opposed to the drift LFO's wobble. See ArcDriver._slowAxisFor. +// +// It has to be declared rather than guessed. Measured across the churning +// scenes, which param you pick decides everything: moving Moiré Grid's `width` +// changes its time-averaged structure by 0.110, and moving its `offset` by +// 0.002. A randomly chosen param is overwhelmingly likely to be the second kind, +// which is why the first version of the slow axis measured as doing nothing at +// all. +export const SLOW_AXIS_FLAG = 'slowAxis'; + /** Valid feature names a `reactive` entry may reference. Lint enforces this. */ export const REACTIVE_FEATURES = [ 'loudness', 'rms', @@ -256,6 +268,15 @@ export function validateModule(module) { } } if (def.bias && typeof def.bias !== 'string') errors.push(`${where}: \`bias\` must be a key name`); + if (def[SLOW_AXIS_FLAG]) { + if (def[RATE_FLAG]) { + errors.push(`${where}: cannot be both \`rate\` and \`slowAxis\` — walking a rate ` + + `param jumps the animation phase (see the RATE_FLAG note above)`); + } + if (def.type === 'palette' || def.type === 'bool' || def.fixed) { + errors.push(`${where}: \`slowAxis\` needs a numeric range to walk`); + } + } } for (const [name, r] of Object.entries(module.reactive || {})) { diff --git a/flow-state/src/scenes/shader/circuit-bloom.js b/flow-state/src/scenes/shader/circuit-bloom.js index fdeae07..b8f9eac 100644 --- a/flow-state/src/scenes/shader/circuit-bloom.js +++ b/flow-state/src/scenes/shader/circuit-bloom.js @@ -18,7 +18,7 @@ export const circuitBloom = { traits: ['shape', 'camera', 'style'], params: { - cells: { type: 'float', range: [2, 14], default: 6, uniform: 'u_cells', bias: 'density' }, + cells: { type: 'float', range: [2, 14], default: 6, uniform: 'u_cells', bias: 'density' }, trace: { type: 'float', range: [0.01, 0.1], default: 0.035,uniform: 'u_trace' }, pads: { type: 'float', range: [0, 1], default: 0.55, uniform: 'u_pads' }, padSize: { type: 'float', range: [0.04, 0.22],default: 0.1, uniform: 'u_padSize' }, diff --git a/flow-state/src/scenes/shader/curl-flow.js b/flow-state/src/scenes/shader/curl-flow.js index 981cf88..51e57a3 100644 --- a/flow-state/src/scenes/shader/curl-flow.js +++ b/flow-state/src/scenes/shader/curl-flow.js @@ -16,7 +16,7 @@ export const curlFlow = { speed: { type: 'float', range: [0.02, 0.5], default: 0.12, uniform: 'u_speed', bias: 'motion', rate: true }, streak: { type: 'float', range: [0, 1], default: 0.55, uniform: 'u_streak' }, contrast: { type: 'float', range: [0.5, 4], default: 1.6, uniform: 'u_contrast' }, - veins: { type: 'float', range: [1, 12], default: 5.0, uniform: 'u_veins', bias: 'density' }, + veins: { type: 'float', range: [1, 12], default: 5.0, uniform: 'u_veins', bias: 'density' }, glow: { type: 'float', range: [0, 1.2], default: 0.35, uniform: 'u_glow', bias: 'energy' }, palette: { type: 'palette', count: 5 }, }, diff --git a/flow-state/src/scenes/shader/firefly-drift.js b/flow-state/src/scenes/shader/firefly-drift.js index 97b8eee..c304b31 100644 --- a/flow-state/src/scenes/shader/firefly-drift.js +++ b/flow-state/src/scenes/shader/firefly-drift.js @@ -13,7 +13,7 @@ export const fireflyDrift = { traits: ['camera', 'style'], params: { - count: { type: 'float', range: [6, 48], default: 22, uniform: 'u_count', bias: 'density' }, + count: { type: 'float', range: [6, 48], default: 22, uniform: 'u_count', bias: 'density' }, speed: { type: 'float', range: [0.05, 0.8], default: 0.3, uniform: 'u_speed', bias: 'motion', rate: true }, glow: { type: 'float', range: [0, 1.2], default: 0.5, uniform: 'u_glow', bias: 'energy' }, jitter: { type: 'float', range: [0, 0.4], default: 0.12, uniform: 'u_jitter' }, diff --git a/flow-state/src/scenes/shader/gate-corridor.js b/flow-state/src/scenes/shader/gate-corridor.js index d8aef9c..9d498f5 100644 --- a/flow-state/src/scenes/shader/gate-corridor.js +++ b/flow-state/src/scenes/shader/gate-corridor.js @@ -19,7 +19,7 @@ export const gateCorridor = { traits: ['shape', 'camera', 'space', 'style'], params: { - gates: { type: 'int', range: [3, 14], default: 8, uniform: 'u_gates', bias: 'density' }, + gates: { type: 'int', range: [3, 14], default: 8, uniform: 'u_gates', bias: 'density' , slowAxis: true }, aperture: { type: 'float', range: [0.15, 0.9], default: 0.45, uniform: 'u_aperture' }, thickness:{ type: 'float', range: [0.02, 0.3], default: 0.09, uniform: 'u_thickness' }, travel: { type: 'float', range: [0.02, 0.7], default: 0.2, uniform: 'u_travel', bias: 'motion', rate: true }, diff --git a/flow-state/src/scenes/shader/kaleido-tunnel.js b/flow-state/src/scenes/shader/kaleido-tunnel.js index fa0f4eb..9f0bc53 100644 --- a/flow-state/src/scenes/shader/kaleido-tunnel.js +++ b/flow-state/src/scenes/shader/kaleido-tunnel.js @@ -15,7 +15,7 @@ export const kaleidoTunnel = { sides: { type: 'int', range: [2, 12], default: 6, uniform: 'u_sides' }, depth: { type: 'float', range: [1, 8], default: 3.0, uniform: 'u_depth', bias: 'density' }, speed: { type: 'float', range: [0.1, 1.5], default: 0.45, uniform: 'u_speed', bias: 'motion', rate: true }, - twist: { type: 'float', range: [0, 2], default: 0.5, uniform: 'u_twist' }, + twist: { type: 'float', range: [0, 2], default: 0.5, uniform: 'u_twist' }, rings: { type: 'float', range: [2, 24], default: 8, uniform: 'u_rings', bias: 'density' }, glow: { type: 'float', range: [0, 1.5], default: 0.5, uniform: 'u_glow', bias: 'energy' }, palette: { type: 'palette', count: 6 }, diff --git a/flow-state/src/scenes/shader/moire-grid.js b/flow-state/src/scenes/shader/moire-grid.js index d36b630..8182410 100644 --- a/flow-state/src/scenes/shader/moire-grid.js +++ b/flow-state/src/scenes/shader/moire-grid.js @@ -21,7 +21,7 @@ export const moireGrid = { // Named u_lineWidth, not u_width: the shader contract already declares // `uniform float u_width` for stereo width, and a colliding name is a // redefinition error that renders the scene as a black frame. - width: { type: 'float', range: [0.06, 0.5], default: 0.2, uniform: 'u_lineWidth' }, + width: { type: 'float', range: [0.06, 0.5], default: 0.2, uniform: 'u_lineWidth' , slowAxis: true }, warp: { type: 'float', range: [0, 1], default: 0.25, uniform: 'u_warp' }, glow: { type: 'float', range: [0, 1.2], default: 0.35, uniform: 'u_glow', bias: 'energy' }, palette: { type: 'palette', count: 4 }, diff --git a/flow-state/src/scenes/shader/plasma-bloom.js b/flow-state/src/scenes/shader/plasma-bloom.js index 8f03202..0628318 100644 --- a/flow-state/src/scenes/shader/plasma-bloom.js +++ b/flow-state/src/scenes/shader/plasma-bloom.js @@ -13,7 +13,7 @@ export const plasmaBloom = { scale: { type: 'float', range: [0.8, 6], default: 2.4, uniform: 'u_scale', bias: 'density' }, warp: { type: 'float', range: [0, 3], default: 1.2, uniform: 'u_warp' }, speed: { type: 'float', range: [0.02, 0.5], default: 0.1, uniform: 'u_speed', bias: 'motion', rate: true }, - bands: { type: 'float', range: [1, 10], default: 3.5, uniform: 'u_bands' }, + bands: { type: 'float', range: [1, 10], default: 3.5, uniform: 'u_bands' }, softness:{ type: 'float', range: [0, 1], default: 0.5, uniform: 'u_softness' }, glow: { type: 'float', range: [0, 1.5], default: 0.4, uniform: 'u_glow', bias: 'energy' }, palette: { type: 'palette', count: 6 }, diff --git a/flow-state/src/scenes/shader/signal-decay.js b/flow-state/src/scenes/shader/signal-decay.js index 796759d..57333c8 100644 --- a/flow-state/src/scenes/shader/signal-decay.js +++ b/flow-state/src/scenes/shader/signal-decay.js @@ -13,7 +13,7 @@ export const signalDecay = { traits: ['camera', 'style'], params: { - traces: { type: 'int', range: [2, 10], default: 5, uniform: 'u_traces', bias: 'density' }, + traces: { type: 'int', range: [2, 10], default: 5, uniform: 'u_traces', bias: 'density' }, amplitude:{ type: 'float', range: [0.02, 0.3], default: 0.1, uniform: 'u_amplitude', bias: 'energy' }, frequency:{ type: 'float', range: [1, 22], default: 7, uniform: 'u_frequency', bias: 'density' }, loss: { type: 'float', range: [0, 0.9], default: 0.35, uniform: 'u_loss' }, diff --git a/flow-state/src/scenes/shader/truchet-fold.js b/flow-state/src/scenes/shader/truchet-fold.js index bb375d9..b1ecdef 100644 --- a/flow-state/src/scenes/shader/truchet-fold.js +++ b/flow-state/src/scenes/shader/truchet-fold.js @@ -17,7 +17,7 @@ export const truchetFold = { params: { cells: { type: 'float', range: [1.5, 12], default: 4, uniform: 'u_cells', bias: 'density' }, - weight: { type: 'float', range: [0.04, 0.3], default: 0.12, uniform: 'u_weight' }, + weight: { type: 'float', range: [0.04, 0.3], default: 0.12, uniform: 'u_weight' , slowAxis: true }, radius: { type: 'float', range: [0.3, 0.7], default: 0.5, uniform: 'u_radius' }, churn: { type: 'float', range: [0, 1], default: 0.35, uniform: 'u_churn' }, glow: { type: 'float', range: [0, 1.4], default: 0.5, uniform: 'u_glow', bias: 'energy' }, diff --git a/flow-state/src/scenes/shader/vortex-drift.js b/flow-state/src/scenes/shader/vortex-drift.js index d63af8c..dbebd42 100644 --- a/flow-state/src/scenes/shader/vortex-drift.js +++ b/flow-state/src/scenes/shader/vortex-drift.js @@ -22,7 +22,7 @@ export const vortexDrift = { speed: { type: 'float', range: [0.02, 0.6], default: 0.15, uniform: 'u_speed', bias: 'motion', rate: true }, turbulence:{ type: 'float', range: [0, 1.2], default: 0.4, uniform: 'u_turbulence' }, core: { type: 'float', range: [0.0, 0.4], default: 0.12, uniform: 'u_core', bias: 'energy' }, - falloff: { type: 'float', range: [0.2, 2.0], default: 0.8, uniform: 'u_falloff' }, + falloff: { type: 'float', range: [0.2, 2.0], default: 0.8, uniform: 'u_falloff' }, palette: { type: 'palette', count: 5 }, },