Effects engine, SVG/VU/silhouette categories, pad bank, MP4 recording, dual-window UI
- Parametric effect engine: 23 shader families × palettes × variants (~2300 presets) with a filterable library and an effect sequence (auto-cycle by time or beat). - Effect categories: fractals/abstract, dancing silhouettes, SVG/Image source (loads any SVG/image as a recolourable audio-reactive effect; 50+ bundled CC0 SVGs), and VU-Meter (32-band spectrum bars, analogue needle, stereo LEDs). - Dual-window architecture: control panel + fullscreen output on an external display (reliable via simple-fullscreen); tabbed control UI (Audio, Effetti, Pad, Immagini, Testo, Registra, Schermo). - Audio: file playback + live input + output device selection, per-band visual EQ, audio playlist (reorder, hotkeys, per-track play/pause, repeat). - Pad bank: 5×4 launchpad with persistent assignments; pressing a pad stops the current. - Overlays: slideshow images (resizable), two freely positioned/resizable logos, scrolling ticker text. - MP4 recording of the composited output (visual + overlays + audio) via MediaRecorder + ffmpeg, with selectable aspect ratios (16:9, 9:16, 1:1, 4:3, 3:4, 21:9). - App icon (.icns) and dock icon; author Dj LuZa. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
b32ea47ae5
commit
47728d0d2a
76 files changed
+21794
-459
No files matched your search
+310
-259
@@ -1,295 +1,346 @@
|
||||
// GLSL ES 3.00 shaders for the visualizer. Stored as strings, compiled at runtime.
|
||||
// Parametric "uber" shader: 16 visual families selected by uFamily, plus a set
|
||||
// of universal modifiers (zoom, rotation, kaleidoscope symmetry, domain warp,
|
||||
// palette, hue cycle, saturation, contrast, invert, audio mix). Combining these
|
||||
// with the effect catalog (effects.js) yields hundreds of distinct looks.
|
||||
|
||||
const VERT = `#version 300 es
|
||||
in vec2 aPos;
|
||||
void main() { gl_Position = vec4(aPos, 0.0, 1.0); }
|
||||
`;
|
||||
|
||||
// Shared fragment-shader prelude: uniforms + helpers, prepended to every scene.
|
||||
const FRAG_HEADER = `#version 300 es
|
||||
const FRAG = `#version 300 es
|
||||
precision highp float;
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform vec2 uRes;
|
||||
uniform float uTime;
|
||||
uniform float uBass; // 0..1 smoothed low band
|
||||
uniform float uMid; // 0..1 smoothed mid band
|
||||
uniform float uTreble; // 0..1 smoothed high band
|
||||
uniform float uLevel; // 0..1 overall loudness
|
||||
uniform float uBeat; // 0..1 decaying pulse on detected beats
|
||||
uniform float uBass, uMid, uTreble, uLevel, uBeat;
|
||||
|
||||
uniform int uFamily;
|
||||
uniform float uScale, uRot, uRotSpeed, uSym;
|
||||
uniform float uHueBase, uHueCycle, uSat, uContrast, uInvert, uWarp, uAudioMix, uSpeed;
|
||||
uniform vec3 uColorA, uColorB;
|
||||
uniform sampler2D uTex; // custom SVG / image source (uploaded flipped-Y)
|
||||
uniform float uSpectrum[32]; // live 32-band spectrum for VU meters
|
||||
uniform float uBgDark; // 1 = force the empty field (v→0) to black
|
||||
|
||||
float uT = 0.0; // time * effect speed (set in main)
|
||||
float aMix = 1.0; // audio mix (set in main)
|
||||
|
||||
mat2 rot(float a) { float s = sin(a), c = cos(a); return mat2(c, -s, s, c); }
|
||||
|
||||
vec3 hsv2rgb(vec3 c) {
|
||||
vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
float hash(vec2 p) {
|
||||
p = fract(p * vec2(123.34, 456.21));
|
||||
p += dot(p, p + 45.32);
|
||||
return fract(p.x * p.y);
|
||||
vec3 rgb2hsv(vec3 c) {
|
||||
vec4 K = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);
|
||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||
}
|
||||
|
||||
float hash(vec2 p) { p = fract(p * vec2(123.34, 456.21)); p += dot(p, p + 45.32); return fract(p.x * p.y); }
|
||||
float noise(vec2 p) {
|
||||
vec2 i = floor(p), f = fract(p);
|
||||
vec2 u = f * f * (3.0 - 2.0 * f);
|
||||
float a = hash(i);
|
||||
float b = hash(i + vec2(1.0, 0.0));
|
||||
float c = hash(i + vec2(0.0, 1.0));
|
||||
float d = hash(i + vec2(1.0, 1.0));
|
||||
float a = hash(i), b = hash(i + vec2(1, 0)), c = hash(i + vec2(0, 1)), d = hash(i + vec2(1, 1));
|
||||
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
||||
}
|
||||
float fbm(vec2 p) { float v = 0.0, a = 0.5; for (int i = 0; i < 6; i++) { v += a * noise(p); p *= 2.02; a *= 0.5; } return v; }
|
||||
|
||||
float fbm(vec2 p) {
|
||||
float v = 0.0, a = 0.5;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
v += a * noise(p);
|
||||
p *= 2.02;
|
||||
a *= 0.5;
|
||||
// ---- Families: each returns a scalar field in roughly [0,1] ----
|
||||
float famJulia(vec2 uv) {
|
||||
vec2 c = vec2(0.7885 * cos(uT * 0.15), 0.7885 * sin(uT * 0.17));
|
||||
c += uTreble * 0.1 * aMix * vec2(sin(uT * 6.0), cos(uT * 5.0));
|
||||
vec2 z = uv * 1.4; float it = 0.0;
|
||||
for (float i = 0.0; i < 128.0; i++) { z = vec2(z.x*z.x - z.y*z.y, 2.0*z.x*z.y) + c; if (dot(z, z) > 16.0) break; it++; }
|
||||
if (it >= 128.0) return 0.0;
|
||||
return (it - log2(log2(dot(z, z))) + 4.0) / 128.0;
|
||||
}
|
||||
float famMandel(vec2 uv) {
|
||||
vec2 c = uv * 1.5 - vec2(0.5, 0.0); vec2 z = vec2(0.0); float it = 0.0;
|
||||
for (float i = 0.0; i < 128.0; i++) { z = vec2(z.x*z.x - z.y*z.y, 2.0*z.x*z.y) + c; if (dot(z, z) > 16.0) break; it++; }
|
||||
if (it >= 128.0) return 0.0;
|
||||
return (it - log2(log2(dot(z, z))) + 4.0) / 128.0;
|
||||
}
|
||||
float famPlasma(vec2 uv) {
|
||||
float t = uT * 0.2;
|
||||
vec2 q = vec2(fbm(uv * 1.5 + t), fbm(uv * 1.5 - t + 5.2));
|
||||
vec2 r = vec2(fbm(uv * 2.0 + 1.7 * q + 0.15 * t), fbm(uv * 2.0 + 1.7 * q + vec2(8.3, 2.8) - 0.12 * t));
|
||||
return fbm(uv * 2.0 + 3.0 * r + uBass * aMix);
|
||||
}
|
||||
float famVortex(vec2 uv) {
|
||||
float r = length(uv), a = atan(uv.y, uv.x);
|
||||
float swirl = a + (1.2 + uBass * 2.0 * aMix) / (r + 0.15) - uT * 0.6;
|
||||
return (sin(swirl * 5.0 + r * 12.0 - uT * 2.0) * 0.5 + 0.5) * (0.4 + 0.8 * fbm(uv * 3.0));
|
||||
}
|
||||
float famWaves(vec2 uv) {
|
||||
float r = length(uv);
|
||||
float energy = mix(uBass, uTreble, clamp(r, 0.0, 1.0)) * aMix + 0.3;
|
||||
return (1.0 - smoothstep(0.0, 0.6, abs(sin(r * 24.0 - uT * 4.0)))) * energy;
|
||||
}
|
||||
float famCells(vec2 uv) {
|
||||
vec2 p = uv * 4.0 + 8.0; vec2 ip = floor(p), fp = fract(p);
|
||||
float d1 = 8.0, d2 = 8.0;
|
||||
for (int y = -1; y <= 1; y++) for (int x = -1; x <= 1; x++) {
|
||||
vec2 g = vec2(float(x), float(y));
|
||||
vec2 o = vec2(hash(ip + g), hash(ip + g + 3.7));
|
||||
float d = length(g + 0.5 + 0.45 * sin(uT * 0.6 + o * 6.283) - fp);
|
||||
if (d < d1) { d2 = d1; d1 = d; } else if (d < d2) d2 = d;
|
||||
}
|
||||
return clamp(d2 - d1, 0.0, 1.0);
|
||||
}
|
||||
float famHyper(vec2 uv) {
|
||||
float v = 0.0, warp = 0.4 + uBass * 2.0 * aMix;
|
||||
for (int i = 0; i < 40; i++) {
|
||||
float fi = float(i);
|
||||
float ang = hash(vec2(fi, 1.0)) * 6.283;
|
||||
float z = fract(hash(vec2(fi, 3.0)) + uT * (0.25 + hash(vec2(fi, 2.0)) * 0.9) * warp);
|
||||
vec2 pos = vec2(cos(ang), sin(ang)) * z * z * 1.6;
|
||||
v += smoothstep(0.06 * z + 0.004, 0.0, length(uv - pos)) * z;
|
||||
}
|
||||
return clamp(v, 0.0, 1.0);
|
||||
}
|
||||
float famTunnel(vec2 uv) {
|
||||
float a = atan(uv.y, uv.x), r = length(uv);
|
||||
float depth = 0.35 / (r + 0.05) + uT * (0.3 + uBass * 1.2 * aMix);
|
||||
float pattern = fbm(vec2(a * 2.0, depth) * 3.0) + 0.5 * sin(a * 8.0 + uT);
|
||||
return clamp(pattern * (sin(depth * 10.0 - uT * 2.0) * 0.5 + 0.5) * smoothstep(0.0, 0.5, r), 0.0, 1.0);
|
||||
}
|
||||
float famMoire(vec2 uv) {
|
||||
float r = length(uv);
|
||||
return (sin(r * 40.0 - uT * 2.0) * sin(dot(uv, uv) * 30.0 + uT) * sin(atan(uv.y, uv.x) * 20.0 + uT)) * 0.5 + 0.5;
|
||||
}
|
||||
float famTruchet(vec2 uv) {
|
||||
vec2 p = uv * 3.0; vec2 fp = fract(p) - 0.5;
|
||||
if (hash(floor(p)) < 0.5) fp.x = -fp.x;
|
||||
float dd = min(abs(length(fp - 0.5) - 0.5), abs(length(fp + 0.5) - 0.5));
|
||||
return smoothstep(0.08, 0.0, dd - 0.02 * sin(uT * 2.0));
|
||||
}
|
||||
float famGyroid(vec2 uv) {
|
||||
vec2 p = uv * 4.0;
|
||||
return (sin(p.x + uT) * cos(p.y) + sin(p.y + uT * 0.7) * cos(p.x * 1.3)) * 0.25 + 0.5;
|
||||
}
|
||||
float famHex(vec2 uv) {
|
||||
vec2 p = uv * 3.0; vec2 h = vec2(1.0, 1.732);
|
||||
vec2 a = mod(p, h) - h * 0.5, b = mod(p - h * 0.5, h) - h * 0.5;
|
||||
vec2 gv = dot(a, a) < dot(b, b) ? a : b;
|
||||
return smoothstep(0.5, 0.45, length(gv) + 0.05 * sin(uT * 2.0));
|
||||
}
|
||||
float famGrid(vec2 uv) {
|
||||
vec2 g = sin(uv * 8.0 + vec2(uT, uT * 1.3));
|
||||
return g.x * g.y * 0.5 + 0.5;
|
||||
}
|
||||
float famClouds(vec2 uv) { return fbm(uv * 2.5 + vec2(uT * 0.1, uT * 0.07)); }
|
||||
float famSpiral(vec2 uv) {
|
||||
float r = length(uv), a = atan(uv.y, uv.x);
|
||||
return sin(6.0 * a + log(r + 0.001) * 6.0 - uT * 2.0) * 0.5 + 0.5;
|
||||
}
|
||||
float famCrystals(vec2 uv) {
|
||||
vec2 p = fract(uv * 2.0) - 0.5;
|
||||
float a = atan(p.y, p.x), r = length(p);
|
||||
float star = cos(a * 5.0 + uT) * 0.2 + 0.3;
|
||||
return smoothstep(star, star - 0.05, r);
|
||||
}
|
||||
|
||||
// ---- Silhouette families (people & objects via signed distance fields) ----
|
||||
float sdSeg(vec2 p, vec2 a, vec2 b, float r) {
|
||||
vec2 pa = p - a, ba = b - a;
|
||||
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
||||
return length(pa - ba * h) - r;
|
||||
}
|
||||
|
||||
// A dancing humanoid: torso, head, swinging arms and stepping legs. t drives
|
||||
// the dance; e (audio energy) raises the arms and adds a vertical bounce.
|
||||
float sdDancer(vec2 p, float t, float e) {
|
||||
float b = 0.06 * e * sin(t * 6.0); // bounce on the beat
|
||||
vec2 hip = vec2(0.0, -0.05 + b);
|
||||
vec2 neck = vec2(0.0, 0.30 + b);
|
||||
vec2 head = vec2(0.0, 0.46 + b);
|
||||
float s = sin(t * 3.0);
|
||||
float raise = e * 0.55;
|
||||
|
||||
float d = sdSeg(p, hip, neck, 0.06); // torso
|
||||
d = min(d, length(p - head) - 0.10); // head
|
||||
|
||||
vec2 shL = neck + vec2(-0.10, 0.0), shR = neck + vec2(0.10, 0.0);
|
||||
vec2 elbL = shL + vec2(-0.12, -0.10 + 0.18 * s + raise);
|
||||
vec2 hndL = elbL + vec2(-0.10, 0.02 + 0.20 * s + raise);
|
||||
vec2 elbR = shR + vec2( 0.12, -0.10 - 0.18 * s + raise);
|
||||
vec2 hndR = elbR + vec2( 0.10, 0.02 - 0.20 * s + raise);
|
||||
d = min(d, sdSeg(p, shL, elbL, 0.045));
|
||||
d = min(d, sdSeg(p, elbL, hndL, 0.038));
|
||||
d = min(d, sdSeg(p, shR, elbR, 0.045));
|
||||
d = min(d, sdSeg(p, elbR, hndR, 0.038));
|
||||
|
||||
vec2 kneeL = hip + vec2(-0.06, -0.22 + 0.06 * s);
|
||||
vec2 footL = kneeL + vec2(-0.02 - 0.06 * s, -0.22);
|
||||
vec2 kneeR = hip + vec2( 0.06, -0.22 - 0.06 * s);
|
||||
vec2 footR = kneeR + vec2( 0.02 + 0.06 * s, -0.22);
|
||||
d = min(d, sdSeg(p, hip, kneeL, 0.05));
|
||||
d = min(d, sdSeg(p, kneeL, footL, 0.04));
|
||||
d = min(d, sdSeg(p, hip, kneeR, 0.05));
|
||||
d = min(d, sdSeg(p, kneeR, footR, 0.04));
|
||||
return d;
|
||||
}
|
||||
|
||||
float famDancers(vec2 uv) {
|
||||
float d = 1e9;
|
||||
float energy = uBass * aMix + 0.3;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
float fi = float(i);
|
||||
vec2 p = (uv - vec2(-0.9 + fi * 0.45, -0.32)) / 0.55;
|
||||
d = min(d, sdDancer(p, uT + fi * 1.3, energy));
|
||||
}
|
||||
return smoothstep(0.02, 0.0, d);
|
||||
}
|
||||
|
||||
float famDancerSolo(vec2 uv) {
|
||||
vec2 p = (uv - vec2(0.0, -0.15)) / 0.95;
|
||||
return smoothstep(0.018, 0.0, sdDancer(p, uT * 1.2, uBass * aMix * 1.2 + 0.4));
|
||||
}
|
||||
|
||||
float sdNote(vec2 p) {
|
||||
float d = length(p * vec2(1.15, 1.0)) - 0.12; // note head
|
||||
d = min(d, sdSeg(p, vec2(0.10, 0.0), vec2(0.10, 0.5), 0.022)); // stem
|
||||
d = min(d, sdSeg(p, vec2(0.10, 0.5), vec2(0.24, 0.40), 0.022)); // flag
|
||||
return d;
|
||||
}
|
||||
|
||||
float famSilhouettes(vec2 uv) {
|
||||
float d = 1e9;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
float fi = float(i);
|
||||
float y = 0.32 * sin(uT + fi * 1.7) + 0.06 * uBeat * aMix;
|
||||
vec2 p = (uv - vec2(-0.8 + fi * 0.32, y)) / (0.5 + 0.15 * sin(fi));
|
||||
d = min(d, sdNote(p));
|
||||
}
|
||||
return smoothstep(0.02, 0.0, d);
|
||||
}
|
||||
|
||||
// Custom source: sample the uploaded SVG/image as the scalar field. Coverage
|
||||
// (alpha) defines the shape; brightness adds inner detail.
|
||||
float famCustom(vec2 uv) {
|
||||
// Fit the square texture to ~70% of screen height (coeff > 1 shrinks it).
|
||||
vec2 tc = uv * 1.45 + 0.5;
|
||||
if (tc.x < 0.0 || tc.x > 1.0 || tc.y < 0.0 || tc.y > 1.0) return 0.0;
|
||||
vec4 t = texture(uTex, tc);
|
||||
float luma = dot(t.rgb, vec3(0.299, 0.587, 0.114));
|
||||
return t.a * (0.55 + 0.45 * luma); // coverage dominates so dark silhouettes stay visible
|
||||
}
|
||||
|
||||
// ---- VU-meter families (use the live spectrum) ----
|
||||
float specAt(int i) { return uSpectrum[i]; }
|
||||
|
||||
// Classic spectrum-analyser bars rising from the bottom.
|
||||
float famVUBars(vec2 uv) {
|
||||
float xn = uv.x * 0.62 + 0.5; // map width to [0,1]
|
||||
if (xn < 0.0 || xn > 1.0) return 0.0;
|
||||
float N = 32.0;
|
||||
int col = int(clamp(xn * N, 0.0, N - 1.0));
|
||||
float level = specAt(col);
|
||||
float yb = (uv.y + 0.46) / 0.92; // 0 bottom -> 1 top
|
||||
if (yb < 0.0 || yb > 1.0) return 0.0;
|
||||
float bx = fract(xn * N);
|
||||
float gap = smoothstep(0.04, 0.12, bx) * smoothstep(0.96, 0.88, bx); // bar spacing
|
||||
float lit = step(yb, level) * gap;
|
||||
// peak cap line just above the level
|
||||
float cap = smoothstep(0.03, 0.0, abs(yb - level)) * gap;
|
||||
return max(lit * (0.2 + 0.8 * yb), cap);
|
||||
}
|
||||
|
||||
// Analogue needle gauge.
|
||||
float famVUNeedle(vec2 uv) {
|
||||
vec2 p = uv - vec2(0.0, -0.28);
|
||||
float r = length(p);
|
||||
float ang = atan(p.x, p.y); // 0 = straight up
|
||||
float na = (clamp(uLevel, 0.0, 1.0) - 0.5) * 1.7 + uBass * 0.15;
|
||||
float needle = smoothstep(0.045, 0.0, abs(ang - na)) * step(r, 0.62) * step(0.04, r);
|
||||
float arc = smoothstep(0.018, 0.0, abs(r - 0.62)) * step(abs(ang), 0.9);
|
||||
float hub = smoothstep(0.06, 0.045, r);
|
||||
// red zone near the top of the scale
|
||||
float redzone = step(0.55, ang) * arc;
|
||||
return max(max(needle, hub), arc * (0.5 + 0.5 * redzone));
|
||||
}
|
||||
|
||||
// Stereo LED level meters (two segmented horizontal bars).
|
||||
float famVUStereo(vec2 uv) {
|
||||
float xn = uv.x * 0.6 + 0.5;
|
||||
if (xn < 0.0 || xn > 1.0) return 0.0;
|
||||
float lvlTop = clamp(uLevel * 1.1, 0.0, 1.0);
|
||||
float lvlBot = clamp((uBass + uTreble) * 0.6, 0.0, 1.0);
|
||||
float seg = step(0.18, fract(xn * 26.0)); // LED gaps
|
||||
float v = 0.0;
|
||||
if (abs(uv.y - 0.12) < 0.07) v = step(xn, lvlTop) * seg * (0.25 + 0.75 * xn);
|
||||
if (abs(uv.y + 0.12) < 0.07) v = step(xn, lvlBot) * seg * (0.25 + 0.75 * xn);
|
||||
return v;
|
||||
}
|
||||
`;
|
||||
|
||||
// ---- Scene 0: Julia / Mandelbrot-style fractal -------------------------
|
||||
const FRAG_FRACTAL = FRAG_HEADER + `
|
||||
float field(int f, vec2 uv) {
|
||||
if (f == 0) return famJulia(uv);
|
||||
if (f == 1) return famMandel(uv);
|
||||
if (f == 2) return famPlasma(uv);
|
||||
if (f == 3) return famVortex(uv);
|
||||
if (f == 4) return famWaves(uv);
|
||||
if (f == 5) return famCells(uv);
|
||||
if (f == 6) return famHyper(uv);
|
||||
if (f == 7) return famTunnel(uv);
|
||||
if (f == 8) return famMoire(uv);
|
||||
if (f == 9) return famTruchet(uv);
|
||||
if (f == 10) return famGyroid(uv);
|
||||
if (f == 11) return famHex(uv);
|
||||
if (f == 12) return famGrid(uv);
|
||||
if (f == 13) return famClouds(uv);
|
||||
if (f == 14) return famSpiral(uv);
|
||||
if (f == 16) return famDancers(uv);
|
||||
if (f == 17) return famDancerSolo(uv);
|
||||
if (f == 18) return famSilhouettes(uv);
|
||||
if (f == 19) return famCustom(uv);
|
||||
if (f == 20) return famVUBars(uv);
|
||||
if (f == 21) return famVUNeedle(uv);
|
||||
if (f == 22) return famVUStereo(uv);
|
||||
return famCrystals(uv); // f == 15
|
||||
}
|
||||
|
||||
vec3 colorize(float v, vec2 uv0) {
|
||||
v = clamp(v, 0.0, 1.0);
|
||||
vec3 hsv = rgb2hsv(mix(uColorA, uColorB, v));
|
||||
hsv.x = fract(hsv.x + uHueBase + uHueCycle * uT + uTreble * 0.06 * aMix);
|
||||
hsv.y = clamp(hsv.y * uSat, 0.0, 1.0);
|
||||
hsv.z = pow(clamp(hsv.z, 0.0, 1.0), uContrast) * (0.7 + 0.6 * uLevel * aMix);
|
||||
vec3 col = hsv2rgb(hsv);
|
||||
if (uInvert > 0.5) col = vec3(1.0) - col;
|
||||
// For silhouette/meter effects, fade the empty field to black.
|
||||
col *= mix(1.0, smoothstep(0.0, 0.04, v), uBgDark);
|
||||
col += uBeat * 0.18 * aMix;
|
||||
col *= 1.0 - 0.28 * dot(uv0, uv0);
|
||||
return col;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 uv = (gl_FragCoord.xy - 0.5 * uRes) / uRes.y;
|
||||
uT = uTime * uSpeed;
|
||||
aMix = uAudioMix;
|
||||
vec2 uv0 = (gl_FragCoord.xy - 0.5 * uRes) / uRes.y;
|
||||
vec2 uv = rot(uRot + uRotSpeed * uT) * uv0;
|
||||
uv /= (uScale * (1.0 + uBass * 0.4 * aMix));
|
||||
|
||||
// Bass-driven zoom breathing + slow drift.
|
||||
float zoom = 1.3 + 0.55 * sin(uTime * 0.07) + uBass * 0.9;
|
||||
uv /= zoom;
|
||||
|
||||
// Slowly wandering Julia constant; treble adds jitter on the path.
|
||||
float t = uTime * 0.15;
|
||||
vec2 c = vec2(0.7885 * cos(t), 0.7885 * sin(t * 1.13));
|
||||
c += (uTreble * 0.12) * vec2(sin(uTime * 6.0), cos(uTime * 5.0));
|
||||
|
||||
vec2 z = uv * 1.4;
|
||||
float iter = 0.0;
|
||||
const float MAX = 160.0;
|
||||
for (float i = 0.0; i < MAX; i++) {
|
||||
z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;
|
||||
if (dot(z, z) > 16.0) break;
|
||||
iter++;
|
||||
if (uSym > 0.5) {
|
||||
float a = atan(uv.y, uv.x), r = length(uv);
|
||||
a = mod(a, 6.2831853 / uSym);
|
||||
a = abs(a - 3.14159265 / uSym);
|
||||
uv = vec2(cos(a), sin(a)) * r;
|
||||
}
|
||||
if (uWarp > 0.001) {
|
||||
uv += uWarp * vec2(fbm(uv * 2.0 + uT * 0.1), fbm(uv * 2.0 - uT * 0.1 + 3.3));
|
||||
}
|
||||
|
||||
// Smooth iteration count for banding-free color.
|
||||
float sm = iter - log2(log2(dot(z, z))) + 4.0;
|
||||
float m = sm / MAX;
|
||||
|
||||
vec3 col;
|
||||
if (iter >= MAX) {
|
||||
col = vec3(0.0);
|
||||
} else {
|
||||
float hue = fract(0.55 + m * 2.5 + uTime * 0.03 + uMid * 0.3);
|
||||
float sat = 0.65 + 0.35 * uTreble;
|
||||
float val = pow(m, 0.45) * (0.7 + 0.6 * uLevel);
|
||||
col = hsv2rgb(vec3(hue, sat, clamp(val, 0.0, 1.2)));
|
||||
}
|
||||
|
||||
// Beat flash + soft vignette.
|
||||
col += uBeat * 0.25 * vec3(0.6, 0.7, 1.0);
|
||||
float vig = 1.0 - 0.35 * dot(uv * zoom, uv * zoom);
|
||||
col *= clamp(vig, 0.0, 1.0);
|
||||
|
||||
fragColor = vec4(col, 1.0);
|
||||
float v = field(uFamily, uv);
|
||||
fragColor = vec4(colorize(v, uv0), 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// ---- Scene 1: Plasma / domain-warped flow ------------------------------
|
||||
const FRAG_PLASMA = FRAG_HEADER + `
|
||||
void main() {
|
||||
vec2 uv = (gl_FragCoord.xy - 0.5 * uRes) / uRes.y;
|
||||
float t = uTime * (0.15 + uMid * 0.4);
|
||||
|
||||
// Domain warping: feed fbm into fbm for liquid motion.
|
||||
vec2 q = vec2(fbm(uv * 1.5 + t), fbm(uv * 1.5 - t + 5.2));
|
||||
vec2 r = vec2(
|
||||
fbm(uv * 2.0 + 1.7 * q + vec2(1.7, 9.2) + 0.15 * t),
|
||||
fbm(uv * 2.0 + 1.7 * q + vec2(8.3, 2.8) - 0.12 * t)
|
||||
);
|
||||
float f = fbm(uv * 2.0 + 3.0 * r + uBass * 1.2);
|
||||
|
||||
float hue = fract(0.6 + f * 0.6 + length(r) * 0.3 + uTime * 0.02);
|
||||
float val = 0.25 + 1.1 * f * (0.5 + uLevel);
|
||||
vec3 col = hsv2rgb(vec3(hue, 0.7 + 0.3 * uTreble, val));
|
||||
|
||||
// Bright filaments where the warp field folds.
|
||||
col += vec3(0.9, 0.95, 1.0) * pow(length(r), 3.0) * (0.4 + uBeat);
|
||||
col *= 0.85 + 0.5 * uBass;
|
||||
|
||||
fragColor = vec4(col, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// ---- Scene 2: Kaleidoscope tunnel --------------------------------------
|
||||
const FRAG_TUNNEL = FRAG_HEADER + `
|
||||
void main() {
|
||||
vec2 uv = (gl_FragCoord.xy - 0.5 * uRes) / uRes.y;
|
||||
|
||||
float a = atan(uv.y, uv.x);
|
||||
float rad = length(uv);
|
||||
|
||||
// Kaleidoscope: fold angle into N reactive segments.
|
||||
float seg = 6.0 + floor(uMid * 8.0);
|
||||
a = abs(mod(a, 6.28318 / seg) - 3.14159 / seg);
|
||||
|
||||
// Tunnel: travel inward, speed pulses with bass.
|
||||
float depth = 0.35 / (rad + 0.05) + uTime * (0.3 + uBass * 1.4);
|
||||
vec2 tuv = vec2(a * 2.0, depth);
|
||||
|
||||
float pattern = fbm(tuv * 3.0) + 0.5 * sin(tuv.x * 8.0 + uTime);
|
||||
float rings = sin(depth * 10.0 - uTime * 2.0) * 0.5 + 0.5;
|
||||
|
||||
float hue = fract(0.5 + depth * 0.05 + pattern * 0.3 + uTreble * 0.4);
|
||||
float val = (0.2 + 0.9 * pattern) * rings * (0.6 + uLevel);
|
||||
// Fade core to black so the tunnel reads as depth.
|
||||
val *= smoothstep(0.0, 0.5, rad);
|
||||
|
||||
vec3 col = hsv2rgb(vec3(hue, 0.8, val));
|
||||
col += uBeat * 0.4 * vec3(1.0, 0.5, 0.9) * (1.0 - rad);
|
||||
|
||||
fragColor = vec4(col, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// ---- Scene 3: Vortice (swirl) ------------------------------------------
|
||||
const FRAG_VORTEX = FRAG_HEADER + `
|
||||
void main() {
|
||||
vec2 uv = (gl_FragCoord.xy - 0.5 * uRes) / uRes.y;
|
||||
float r = length(uv);
|
||||
float a = atan(uv.y, uv.x);
|
||||
|
||||
// Spiral arms that twist harder near the centre and spin with the bass.
|
||||
float swirl = a + (1.2 + uBass * 3.0) / (r + 0.15) - uTime * (0.6 + uMid);
|
||||
float arms = sin(swirl * 5.0 + r * 12.0 - uTime * 2.0) * 0.5 + 0.5;
|
||||
float n = fbm(uv * 3.0 + vec2(cos(uTime * 0.2), sin(uTime * 0.2)));
|
||||
float v = arms * (0.4 + 0.8 * n);
|
||||
|
||||
float hue = fract(0.72 + r * 0.35 + uTime * 0.04 + uTreble * 0.3);
|
||||
vec3 col = hsv2rgb(vec3(hue, 0.85, v * (0.5 + uLevel)));
|
||||
col += uBeat * 0.3 * vec3(1.0, 0.6, 0.9) * (1.0 - r);
|
||||
col *= smoothstep(0.0, 0.15, r); // dark eye in the middle
|
||||
fragColor = vec4(col, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// ---- Scene 4: Onde (reactive frequency rings) --------------------------
|
||||
const FRAG_WAVES = FRAG_HEADER + `
|
||||
void main() {
|
||||
vec2 uv = (gl_FragCoord.xy - 0.5 * uRes) / uRes.y;
|
||||
float r = length(uv);
|
||||
float a = atan(uv.y, uv.x);
|
||||
|
||||
// Energy mapped radially: bass at the core, treble at the edges.
|
||||
float energy = mix(uBass, mix(uMid, uTreble, smoothstep(0.4, 1.0, r)), smoothstep(0.0, 0.5, r));
|
||||
|
||||
// Outgoing rings whose spacing breathes with the beat.
|
||||
float rings = sin(r * (24.0 + uBeat * 20.0) - uTime * 4.0);
|
||||
rings = smoothstep(0.0, 0.6, abs(rings));
|
||||
rings = 1.0 - rings;
|
||||
|
||||
// Angular petals add structure to the rings.
|
||||
float petals = 0.5 + 0.5 * sin(a * (8.0 + floor(uMid * 10.0)) + uTime);
|
||||
|
||||
float glow = rings * (0.3 + 1.4 * energy) * (0.5 + 0.7 * petals);
|
||||
float hue = fract(0.55 + r * 0.5 - uTime * 0.05 + energy * 0.4);
|
||||
vec3 col = hsv2rgb(vec3(hue, 0.8, glow));
|
||||
col += energy * uBeat * vec3(0.6, 0.8, 1.0) * (1.0 - r);
|
||||
fragColor = vec4(col, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// ---- Scene 5: Cellule (animated voronoi) -------------------------------
|
||||
const FRAG_CELLS = FRAG_HEADER + `
|
||||
vec2 cellPoint(vec2 ip, float t) {
|
||||
// A wandering feature point inside cell ip.
|
||||
vec2 o = vec2(hash(ip), hash(ip + 3.7));
|
||||
return 0.5 + 0.45 * sin(t * (0.4 + o * 0.8) + o * 6.283);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 uv = (gl_FragCoord.xy - 0.5 * uRes) / uRes.y;
|
||||
float scale = 4.0 - uBass * 1.5; // cells swell on the bass
|
||||
vec2 p = uv * scale + 8.0;
|
||||
vec2 ip = floor(p), fp = fract(p);
|
||||
|
||||
float d1 = 8.0, d2 = 8.0; // nearest + second-nearest
|
||||
vec2 nearId = vec2(0.0);
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
vec2 g = vec2(float(x), float(y));
|
||||
vec2 pt = g + cellPoint(ip + g, uTime * (0.6 + uMid));
|
||||
float d = length(pt - fp);
|
||||
if (d < d1) { d2 = d1; d1 = d; nearId = ip + g; }
|
||||
else if (d < d2) { d2 = d; }
|
||||
}
|
||||
}
|
||||
|
||||
float edge = smoothstep(0.0, 0.08 + uTreble * 0.05, d2 - d1); // bright borders
|
||||
float cellHue = fract(hash(nearId) + uTime * 0.03 + uMid * 0.2);
|
||||
vec3 base = hsv2rgb(vec3(cellHue, 0.7, 0.25 + 0.6 * (1.0 - d1)));
|
||||
vec3 col = base * (0.4 + 0.6 * edge);
|
||||
col += (1.0 - edge) * uBeat * 0.5 * vec3(1.0, 0.9, 0.7);
|
||||
col *= 0.7 + 0.6 * uLevel;
|
||||
fragColor = vec4(col, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// ---- Scene 6: Iperspazio (starfield warp) ------------------------------
|
||||
const FRAG_HYPER = FRAG_HEADER + `
|
||||
void main() {
|
||||
vec2 uv = (gl_FragCoord.xy - 0.5 * uRes) / uRes.y;
|
||||
vec3 col = vec3(0.0);
|
||||
float warp = 0.4 + uBass * 2.2; // travel speed pulses with the bass
|
||||
|
||||
for (int i = 0; i < 48; i++) {
|
||||
float fi = float(i);
|
||||
float ang = hash(vec2(fi, 1.0)) * 6.2831853;
|
||||
float spd = 0.25 + hash(vec2(fi, 2.0)) * 0.9;
|
||||
float z = fract(hash(vec2(fi, 3.0)) + uTime * spd * warp);
|
||||
float rad = z * z * 1.6; // accelerate outward
|
||||
vec2 pos = vec2(cos(ang), sin(ang)) * rad;
|
||||
float d = length(uv - pos);
|
||||
// Stretch the star into a streak along its travel direction.
|
||||
float streak = smoothstep(0.06 * z + 0.004, 0.0, d) * z;
|
||||
float hue = fract(0.6 + hash(vec2(fi, 4.0)) * 0.3 + uTreble * 0.3);
|
||||
col += hsv2rgb(vec3(hue, 0.5 + 0.5 * uMid, 1.0)) * streak;
|
||||
}
|
||||
col *= 0.7 + 0.8 * uLevel;
|
||||
col += uBeat * 0.15;
|
||||
fragColor = vec4(col, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// ---- Scene 7: Specchi (kaleidoscope plasma) ----------------------------
|
||||
const FRAG_MIRRORS = FRAG_HEADER + `
|
||||
void main() {
|
||||
vec2 uv = (gl_FragCoord.xy - 0.5 * uRes) / uRes.y;
|
||||
|
||||
// Fold space into a reactive number of mirrored wedges.
|
||||
float seg = 4.0 + 2.0 * floor(1.0 + uMid * 5.0);
|
||||
float a = atan(uv.y, uv.x);
|
||||
float r = length(uv);
|
||||
a = mod(a, 6.2831853 / seg);
|
||||
a = abs(a - 3.1415926 / seg);
|
||||
vec2 muv = vec2(cos(a), sin(a)) * r;
|
||||
|
||||
// Plasma inside the wedge, scrolling with the beat.
|
||||
float t = uTime * (0.2 + uBass * 0.5);
|
||||
vec2 q = vec2(fbm(muv * 2.5 + t), fbm(muv * 2.5 - t + 4.0));
|
||||
float f = fbm(muv * 3.0 + 2.0 * q + uBeat);
|
||||
|
||||
float hue = fract(0.5 + f * 0.6 + r * 0.4 + uTime * 0.03);
|
||||
vec3 col = hsv2rgb(vec3(hue, 0.8 + 0.2 * uTreble, 0.2 + 1.1 * f * (0.5 + uLevel)));
|
||||
col += pow(length(q), 2.5) * vec3(0.9, 0.95, 1.0) * (0.4 + uBeat);
|
||||
fragColor = vec4(col, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
window.SHADERS = {
|
||||
vert: VERT,
|
||||
names: ['Frattale', 'Plasma', 'Tunnel', 'Vortice', 'Onde', 'Cellule', 'Iperspazio', 'Specchi'],
|
||||
scenes: [FRAG_FRACTAL, FRAG_PLASMA, FRAG_TUNNEL, FRAG_VORTEX, FRAG_WAVES, FRAG_CELLS, FRAG_HYPER, FRAG_MIRRORS]
|
||||
};
|
||||
window.SHADERS = { vert: VERT, frag: FRAG };
|
||||
Reference in new issue
Block a user