From c1b3f9c3119005a0790343fa13a0b0b7598df943 Mon Sep 17 00:00:00 2001 From: luciano Date: Thu, 30 Jul 2026 10:24:45 +0200 Subject: [PATCH] Onda 3D family: silk-ribbon wireframe of the live waveform Two bands of fine strands (iso-lines of the band coordinate) flow across the screen, compressing into moire at the folds like a 3D wireframe surface. Hue ramps across each band through magenta (warm crest, cool belly). The centre curve mixes drifting sines with the live waveform; bass swells the band, beat brightens. Shader family index 57; in the Auto VJ quiet/groove pools. wamp() now interpolates between waveform samples to avoid stair-step artifacts (also smooths Onda Luminosa). Co-Authored-By: Claude Fable 5 --- src/effects.js | 3 ++- src/output.js | 4 ++-- src/shaders.js | 45 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/effects.js b/src/effects.js index ac47ee8..662e062 100644 --- a/src/effects.js +++ b/src/effects.js @@ -71,7 +71,8 @@ const FAMILIES = [ // Cinematic audio-light scenic families (shader indices 55-56): the live // waveform / spectrum drawn as beams of light with flares and bokeh. { name: 'Onda Luminosa', scale: 1.0 }, - { name: 'Spettro Neon', scale: 1.0 } + { name: 'Spettro Neon', scale: 1.0 }, + { name: 'Onda 3D', scale: 1.0 } ]; // Palettes: low colour (a) -> high colour (b), plus optional hue-cycle/sat. diff --git a/src/output.js b/src/output.js index fd5cf4a..43aabd3 100644 --- a/src/output.js +++ b/src/output.js @@ -483,10 +483,10 @@ let frames = 0, fpsT = performance.now(), fps = 0, lastReport = 0, prevBeat = 0, let autoVj = false, avLastSwitch = 0, avBeatMark = 0, avRecent = []; const AV_POOLS = { quiet: ['Cielo', 'Aurora', 'Mare', 'Montagne', 'Galassia', 'Nuvole', 'Fluido', 'Onde', - 'Onda Luminosa'], + 'Onda Luminosa', 'Onda 3D'], groove: ['Plasma', 'Vortice', 'Truchet', 'Esagoni', 'Spirale', 'Cellule', 'Gyroide', 'Fluido', 'Fluido Onda', 'Fluido Vortici', 'Griglia Neon', 'Solidi 3D', 'Tunnel Neon', - 'Onda Luminosa', 'Spettro Neon'], + 'Onda Luminosa', 'Spettro Neon', 'Onda 3D'], peak: ['Julia', 'Iperspazio', 'Tunnel', 'Moiré', 'Fluido Anello', 'Fluido Fuoco', 'Vortice', 'Griglia Neon', 'Cristalli', 'Tunnel Neon', 'Tempesta Plasma', 'Spettro Neon'] }; diff --git a/src/shaders.js b/src/shaders.js index 1954d52..e82bdb8 100644 --- a/src/shaders.js +++ b/src/shaders.js @@ -596,8 +596,10 @@ vec3 scSupernova(vec2 uv) { float wamp(float xn) { float fx = clamp(xn, 0.0, 1.0) * 255.0; int i0 = int(floor(fx)); - return abs(waveAt(i0)) * 0.5 - + (abs(waveAt(min(i0 + 4, 255))) + abs(waveAt(max(i0 - 4, 0)))) * 0.25; + // linear interpolation avoids per-sample stair-steps in the curves + float a = mix(abs(waveAt(i0)), abs(waveAt(min(i0 + 1, 255))), fract(fx)); + float b = mix(abs(waveAt(max(i0 - 5, 0))), abs(waveAt(min(i0 + 5, 255))), 0.5); + return a * 0.6 + b * 0.4; } // Soft out-of-focus dots drifting slowly (two layers are combined by callers). vec3 scBokeh(vec2 uv, float n, float th, float soft) { @@ -693,6 +695,44 @@ vec3 scSpettro(vec2 uv) { return col; } +// 3D silk ribbon (family 57): two bands of fine strands flowing across the +// screen. Inside each band, iso-lines of the band coordinate s become the +// strands: where the band folds thin they compress into dense moiré, where it +// opens wide they separate — the wireframe-surface look of the reference. +// Hue ramps across the band (warm crest -> cool belly, palette-shifted). +vec3 scOnda3D(vec2 uv) { + vec3 col = vec3(0.0); + float xn = clamp(uv.x * 0.5 + 0.5, 0.0, 1.0); + float w = wamp(xn) * (0.5 + 0.8 * uBass * aMix); + vec3 hb = rgb2hsv(uColorB); + for (int L = 0; L < 2; L++) { + float fl = float(L); // 0 = back ribbon (dim), 1 = front + float ph = fl * 2.7; + // centre curve: layered drifting sines + the live waveform wiggle + float yc = 0.16 * sin(uv.x * 2.1 + uT * 0.7 + ph) + + 0.09 * sin(uv.x * 4.7 - uT * 1.1 + ph * 1.7) + + 0.05 * sin(uv.x * 9.3 + uT * 1.9 + ph * 0.6) + + 0.45 * w * sin(uv.x * 6.0 - uT * 3.0 + ph); + // band thickness breathes and swells with the music + float th = max(0.10 + 0.07 * sin(uv.x * 3.3 + uT * 0.9 + ph * 2.1) + 0.14 * w, 0.025); + float s = (uv.y - yc) / th * 0.5 + 0.5; // 0..1 across the band + if (s < -0.05 || s > 1.05) continue; + float in01 = smoothstep(0.0, 0.06, s) * smoothstep(1.0, 0.94, s); + // fine strands: iso-lines of s (thin when the band is folded tight) + float fr = abs(fract(s * 28.0) - 0.5) * 2.0; + float line = exp(-fr * fr * 9.0); + // brighter rims at both edges of the band, like the reference + float edge = 0.45 + 0.75 * pow(abs(s * 2.0 - 1.0), 2.0); + // hue ramp across the band: crest keeps the palette hue, the belly shifts + // the other way round the wheel (through magenta, like the reference) + vec3 sc = hsv2rgb(vec3(fract(hb.x - (1.0 - s) * 0.45), max(hb.y, 0.55), 1.0)); + float amp = (0.5 + 0.9 * fl) * (0.7 + 0.5 * uLevel * aMix + 0.3 * uBeat * aMix); + col += sc * line * in01 * edge * amp; + col += sc * in01 * 0.05 * amp; // faint fill so the band reads as a surface + } + return col; +} + vec3 fieldRGB(int f, vec2 uv) { if (f == 34) return scCielo(uv); if (f == 35) return scAurora(uv); @@ -706,6 +746,7 @@ vec3 fieldRGB(int f, vec2 uv) { // 44-54 are the camera-interactive families (they never reach the shader). if (f == 55) return scOnda(uv); if (f == 56) return scSpettro(uv); + if (f == 57) return scOnda3D(uv); return scSupernova(uv); // f == 43 }