diff --git a/party-stage/src/visualizers/FloatingShapesVisualizer.js b/party-stage/src/visualizers/FloatingShapesVisualizer.js new file mode 100644 index 0000000..1aef282 --- /dev/null +++ b/party-stage/src/visualizers/FloatingShapesVisualizer.js @@ -0,0 +1,111 @@ +import { VisualizerInterface } from './VisualizerInterface.js'; + +const fragmentShader = ` +uniform float u_time; +uniform float u_beat; +uniform float u_opacity; +uniform vec2 u_resolution; +uniform vec3 u_colors[16]; +uniform int u_colorCount; +uniform float u_seed; +varying vec2 vUv; + +float sdCircle(vec2 p, float r) { + return length(p) - r; +} + +float sdBox(vec2 p, vec2 b) { + vec2 d = abs(p) - b; + return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); +} + +vec2 rotate(vec2 p, float a) { + float s = sin(a); + float c = cos(a); + return mat2(c, -s, s, c) * p; +} + +void main() { + // LED Grid Setup + float ledCountX = u_resolution.x; + float ledCountY = u_resolution.y; + vec2 gridUV = vec2(vUv.x * ledCountX, vUv.y * ledCountY); + vec2 gridDeriv = fwidth(gridUV); + float gridDensity = max(gridDeriv.x, gridDeriv.y); + float blurFactor = smoothstep(0.3, 0.8, gridDensity); + vec2 cell = fract(gridUV); + vec2 uv = mix((floor(gridUV) + 0.5) / vec2(ledCountX, ledCountY), vUv, blurFactor); + + float distMask = distance(cell, vec2(0.5)); + float edgeSoftness = clamp(gridDensity * 1.5, 0.0, 0.5); + float mask = 1.0 - smoothstep(0.35 - edgeSoftness, 0.45 + edgeSoftness, distMask); + mask = mix(mask, 1.0, blurFactor); + + // Aspect correction for shapes + vec2 p = (uv - 0.5); + p.x *= u_resolution.x / u_resolution.y; + + float t = u_time * 0.4 + u_seed; + float beatImpact = u_beat * 0.1; + + // Background color shift + vec3 colorA = vec3(0.05, 0.05, 0.1); + vec3 colorB = vec3(0.1, 0.0, 0.2); + if (u_colorCount >= 2) { + colorA = u_colors[0] * 0.2; + colorB = u_colors[int(mod(1.0 + floor(t * 0.1), float(u_colorCount)))] * 0.3; + } + vec3 finalColor = mix(colorA, colorB, sin(t) * 0.5 + 0.5); + + // Render shapes + float shapes = 0.0; + for (int i = 0; i < 6; i++) { + float fi = float(i); + float shapeSeed = u_seed + fi * 123.456; + + // Motion + vec2 pos = vec2( + sin(t * 0.5 + shapeSeed) * 0.8, + cos(t * 0.3 + shapeSeed * 1.1) * 0.4 + ); + + vec2 sp = p - pos; + sp = rotate(sp, t * (0.5 + fract(shapeSeed))); + + float d; + float size = 0.1 + fract(shapeSeed * 0.7) * 0.2 + beatImpact; + + if (mod(fi, 2.0) == 0.0) { + d = sdCircle(sp, size); + } else { + d = sdBox(sp, vec2(size * 0.8)); + } + + float intensity = smoothstep(0.01, 0.0, d); + + // Pick a color for the shape + vec3 shapeColor = vec3(1.0); + if (u_colorCount > 0) { + int colorIdx = int(mod(fi + floor(t), float(u_colorCount))); + shapeColor = u_colors[colorIdx]; + } + + finalColor = mix(finalColor, shapeColor, intensity * 0.8); + + // Add a slight glow/aura + finalColor += shapeColor * (1.0 - smoothstep(0.0, size * 2.0, abs(d))) * 0.2 * (0.5 + u_beat); + } + + gl_FragColor = vec4(finalColor, mask * u_opacity); +} +`; + +export class FloatingShapesVisualizer extends VisualizerInterface { + getName() { + return "Floating Geometry"; + } + + getFragmentShader() { + return fragmentShader; + } +} \ No newline at end of file diff --git a/party-stage/src/visualizers/SynthwaveRunVisualizer.js b/party-stage/src/visualizers/SynthwaveRunVisualizer.js new file mode 100644 index 0000000..8ff3f4a --- /dev/null +++ b/party-stage/src/visualizers/SynthwaveRunVisualizer.js @@ -0,0 +1,138 @@ +import { VisualizerInterface } from './VisualizerInterface.js'; + +const fragmentShader = ` +uniform float u_time; +uniform float u_beat; +uniform float u_opacity; +uniform vec2 u_resolution; +uniform vec3 u_colors[16]; +uniform int u_colorCount; +uniform float u_seed; +varying vec2 vUv; + +float hash(float n) { return fract(sin(n) * 43758.5453123); } + +float noise(vec2 p) { + vec2 i = floor(p); + vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + float n = i.x + i.y * 57.0; + return mix(mix(hash(n + 0.0), hash(n + 1.0), f.x), + mix(hash(n + 57.0), hash(n + 58.0), f.x), f.y); +} + +void main() { + // LED Grid Setup + float ledCountX = u_resolution.x; + float ledCountY = u_resolution.y; + vec2 gridUV = vec2(vUv.x * ledCountX, vUv.y * ledCountY); + vec2 gridDeriv = fwidth(gridUV); + float gridDensity = max(gridDeriv.x, gridDeriv.y); + float blurFactor = smoothstep(0.3, 0.8, gridDensity); + vec2 cell = fract(gridUV); + vec2 uv = mix((floor(gridUV) + 0.5) / vec2(ledCountX, ledCountY), vUv, blurFactor); + + float distMask = distance(cell, vec2(0.5)); + float edgeSoftness = clamp(gridDensity * 1.5, 0.0, 0.5); + float mask = 1.0 - smoothstep(0.35 - edgeSoftness, 0.45 + edgeSoftness, distMask); + mask = mix(mask, 1.0, blurFactor); + + // --- Synthwave Logic --- + vec2 p = uv * 2.0 - 1.0; + p.x *= u_resolution.x / u_resolution.y; + + float t = u_time * 1.5 + u_seed; + float beat = u_beat; + + // Colors + vec3 colorMain = vec3(1.0, 0.0, 0.8); // Pink/Magenta + vec3 colorSec = vec3(0.0, 0.8, 1.0); // Cyan + vec3 colorBg = vec3(0.02, 0.0, 0.05); + + if (u_colorCount >= 2) { + colorMain = u_colors[0]; + colorSec = u_colors[int(mod(1.0, float(u_colorCount)))]; + } + + vec3 finalColor = colorBg; + + // Horizon + float horizon = 0.0; + + // 1. Perspective Grid (Ground) + if (p.y < horizon) { + float perspective = 1.0 / (horizon - p.y + 0.05); + vec2 gridP = vec2(p.x * perspective, perspective + t * 2.0); + + float gridLines = smoothstep(0.05, 0.0, abs(fract(gridP.x + 0.5) - 0.5)) + + smoothstep(0.05, 0.0, abs(fract(gridP.y + 0.5) - 0.5)); + + finalColor = mix(finalColor, colorSec, gridLines * 0.5 * (0.5 + beat)); + + // Road highlight + if (abs(p.x * perspective) < 0.8) { + finalColor += colorMain * 0.2 * perspective; + } + } + + // 2. Wireframe Mountains + float mountainHeight = 0.0; + if (p.y > horizon - 0.1) { + float side = sign(p.x); + float mountainX = abs(p.x) - 0.5; + if (mountainX > 0.0) { + float mNoise = noise(vec2(mountainX * 2.0 + t * 0.1, u_seed)); + mountainHeight = mNoise * 0.6 * smoothstep(0.0, 0.5, mountainX); + + if (p.y - horizon < mountainHeight) { + float distToEdge = abs(p.y - horizon - mountainHeight); + float wireframe = smoothstep(0.02, 0.0, distToEdge); + + // Internal wireframe lines + wireframe += smoothstep(0.01, 0.0, abs(fract((p.y - horizon) * 10.0) - 0.5)); + wireframe += smoothstep(0.01, 0.0, abs(fract(mountainX * 10.0) - 0.5)); + + finalColor = mix(finalColor, colorMain, wireframe * 0.4); + } + } + } + + // 3. Retro Sun + vec2 sunPos = vec2(0.0, 0.1); + float sunRad = 0.4; + float distToSun = length(p - sunPos); + if (distToSun < sunRad && p.y > horizon) { + // Scanlines / Cutouts + float scanline = sin((p.y - t * 0.1) * 50.0); + if (scanline < mix(0.8, -1.0, (p.y - sunPos.y + sunRad) / (sunRad * 2.0))) { + float grad = 1.0 - (distToSun / sunRad); + vec3 sunCol = mix(colorMain, vec3(1.0, 0.8, 0.0), p.y * 2.0); + finalColor = mix(finalColor, sunCol, 0.8 + beat * 0.2); + } + } + + // 4. Car Silhouette (Simplified) + vec2 carP = p - vec2(0.0, -0.75); + carP.x = abs(carP.x); + if (carP.y > -0.1 && carP.y < 0.1 && carP.x < 0.25) { + float body = step(carP.x, 0.25) * step(abs(carP.y), 0.05); + float cabin = step(carP.x, 0.15) * step(carP.y, 0.1) * step(0.0, carP.y); + if (body + cabin > 0.0) { + finalColor = mix(finalColor, vec3(0.0), 0.9); + finalColor += colorSec * 0.5 * body * (smoothstep(0.0, 0.05, abs(carP.x - 0.2))); // Tail lights / glow + } + } + + gl_FragColor = vec4(finalColor, mask * u_opacity); +} +`; + +export class SynthwaveRunVisualizer extends VisualizerInterface { + getName() { + return "Synthwave Run"; + } + + getFragmentShader() { + return fragmentShader; + } +} \ No newline at end of file diff --git a/party-stage/src/visualizers/index.js b/party-stage/src/visualizers/index.js index bcfa0e1..ad09a09 100644 --- a/party-stage/src/visualizers/index.js +++ b/party-stage/src/visualizers/index.js @@ -1,9 +1,13 @@ import { ClassicVisualizer } from './ClassicVisualizer.js'; import { NebulaVisualizer } from './NebulaVisualizer.js'; +import { FloatingShapesVisualizer } from './FloatingShapesVisualizer.js'; +import { SynthwaveRunVisualizer } from './SynthwaveRunVisualizer.js'; export const visualizerLibrary = [ new ClassicVisualizer(), new NebulaVisualizer(), + new FloatingShapesVisualizer(), + new SynthwaveRunVisualizer(), ]; export default visualizerLibrary;