Feature: Synthwave visualizer
This commit is contained in:
parent
c6324d3ccd
commit
ec2465222c
111
party-stage/src/visualizers/FloatingShapesVisualizer.js
Normal file
111
party-stage/src/visualizers/FloatingShapesVisualizer.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
138
party-stage/src/visualizers/SynthwaveRunVisualizer.js
Normal file
138
party-stage/src/visualizers/SynthwaveRunVisualizer.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,13 @@
|
|||||||
import { ClassicVisualizer } from './ClassicVisualizer.js';
|
import { ClassicVisualizer } from './ClassicVisualizer.js';
|
||||||
import { NebulaVisualizer } from './NebulaVisualizer.js';
|
import { NebulaVisualizer } from './NebulaVisualizer.js';
|
||||||
|
import { FloatingShapesVisualizer } from './FloatingShapesVisualizer.js';
|
||||||
|
import { SynthwaveRunVisualizer } from './SynthwaveRunVisualizer.js';
|
||||||
|
|
||||||
export const visualizerLibrary = [
|
export const visualizerLibrary = [
|
||||||
new ClassicVisualizer(),
|
new ClassicVisualizer(),
|
||||||
new NebulaVisualizer(),
|
new NebulaVisualizer(),
|
||||||
|
new FloatingShapesVisualizer(),
|
||||||
|
new SynthwaveRunVisualizer(),
|
||||||
];
|
];
|
||||||
|
|
||||||
export default visualizerLibrary;
|
export default visualizerLibrary;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user