Feature: Drugged Out Flow visualizer

This commit is contained in:
Dejvino 2026-05-24 14:41:15 +02:00
parent 887d3970ab
commit 070f4d6e01
2 changed files with 208 additions and 0 deletions

View File

@ -0,0 +1,206 @@
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;
// --- Utility Functions ---
float hash(float n) { return fract(sin(n) * 43758.5453123); }
vec2 rotate(vec2 p, float a) {
float s = sin(a);
float c = cos(a);
return mat2(c, -s, s, c) * p;
}
// --- SDFs for Shapes ---
float sdCircle(vec2 p, float r) {
return length(p) - r;
}
float sdStar(vec2 p, float r, float spikes) {
float an = atan(p.y, p.x) / (2.0 * 3.14159265359);
an = fract(an * spikes);
float r2 = r * mix(0.6, 1.0, step(0.5, an));
return length(p) - r2;
}
float sdSmiley(vec2 p, float r, float t, float id) {
float d = sdCircle(p, r);
// Eyes: Bigger and higher. Animate blinking.
float eyeBlink = smoothstep(0.9, 0.95, sin(t * 0.5 + id * 1.2));
float eyeR = 0.18 * r * (1.0 - eyeBlink * 0.9);
vec2 eyePos = vec2(0.3, 0.35) * r;
d = max(d, -sdCircle(vec2(abs(p.x) - eyePos.x, p.y - eyePos.y), eyeR));
// Mouth: Changes shape (Smile/Surprise/Small mouth)
float mouthType = sin(t * 1.2 + id);
if (mouthType > 0.6) { // Surprise
d = max(d, -sdCircle(p - vec2(0.0, -0.2) * r, 0.15 * r));
} else if (mouthType > -0.6) { // Smile
float mouth = sdCircle(p - vec2(0.0, -0.1) * r, 0.45 * r);
mouth = max(mouth, -(p.y - (0.05 + 0.1 * u_beat) * r)); // Open more on beat
d = max(d, -mouth);
} else { // Small mouth
d = max(d, -sdCircle(p - vec2(0.0, -0.25) * r, 0.08 * r));
}
return d;
}
float distToSegment(vec2 p, vec2 a, vec2 b) {
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);
}
// --- Main Shader ---
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
vec2 p = (uv - 0.5);
p.x *= u_resolution.x / u_resolution.y;
float t = u_time * 0.2 + u_seed;
float beat = u_beat;
// --- Background: Swirling psychedelic colors ---
vec3 colorA = vec3(0.05, 0.0, 0.1);
vec3 colorB = vec3(0.1, 0.0, 0.2);
if (u_colorCount >= 2) {
colorA = u_colors[0] * 0.1;
colorB = u_colors[int(mod(1.0, float(u_colorCount)))] * 0.15;
}
float bgNoise = sin(p.x * 3.0 + t) * cos(p.y * 3.0 - t * 0.7) + sin(length(p) * 4.0 - t);
vec3 finalColor = mix(colorA, colorB, bgNoise * 0.5 + 0.5);
finalColor += colorA * 0.05 * beat; // Flash background on beat
// --- Scene Switching Logic ---
float sceneDuration = 15.0;
float sceneIndex = floor(t / sceneDuration);
float currentSceneSeed = u_seed + sceneIndex * 100.0;
// --- "Splitting Eyes" Distortion ---
vec2 distortionCenter = vec2(sin(t * 0.3 + currentSceneSeed) * 0.5, cos(t * 0.4 + currentSceneSeed * 1.1) * 0.5);
float distortionRadius = 0.35 + beat * 0.15;
float distToDistortion = length(p - distortionCenter);
vec2 distortedP = p;
if (distToDistortion < distortionRadius) {
float strength = smoothstep(distortionRadius, 0.0, distToDistortion);
distortedP += normalize(p - distortionCenter) * strength * (0.15 + beat * 0.2);
}
// --- Pre-calculate Lasers for interaction ---
vec2 lp1[3], lp2[3];
for (int j = 0; j < 3; j++) {
float laserSeed = currentSceneSeed + float(j) * 789.0;
lp1[j] = vec2(sin(t * 0.4 + laserSeed) * 0.8, cos(t * 0.3 + laserSeed * 1.1) * 0.5);
lp2[j] = vec2(sin(t * 0.5 + laserSeed * 1.5) * 0.8, cos(t * 0.4 + laserSeed * 1.7) * 0.5);
}
// --- Render Symbols ---
int numSymbols = 8 + int(mod(currentSceneSeed, 16.0));
for (int i = 0; i < 24; i++) {
if (i >= numSymbols) break;
float fi = float(i);
float symbolSeed = currentSceneSeed + fi * 123.456;
// Motion: Orbital + attraction to distortion center
float speed = 0.3 + hash(symbolSeed) * 0.4;
vec2 basePos = vec2(
sin(t * speed + symbolSeed) * 0.8,
cos(t * speed * 0.8 + symbolSeed * 1.4) * 0.5
);
vec2 pos = mix(basePos, distortionCenter, 0.15 + beat * 0.1);
vec2 sp = distortedP - pos;
sp = rotate(sp, t * (0.1 + fract(symbolSeed * 0.7)));
float d;
float size = 0.06 + fract(symbolSeed * 0.3) * 0.04 + beat * 0.02;
// Determine symbol type based on seed
if (mod(fi + sceneIndex, 3.0) == 0.0) { // Stars
d = sdStar(sp, size, 5.0 + floor(hash(symbolSeed * 1.2) * 3.0));
} else if (mod(fi + sceneIndex, 3.0) == 1.0) { // Smilies
d = sdSmiley(sp, size, u_time, fi);
} else { // Circles
d = sdCircle(sp, size);
}
// Interaction: Check proximity to lasers
float laserHit = 0.0;
for (int j = 0; j < 3; j++) {
float distToLaser = distToSegment(pos, lp1[j], lp2[j]);
laserHit = max(laserHit, smoothstep(0.15, 0.0, distToLaser));
}
float intensity = smoothstep(0.01, 0.0, d);
// Pick a color
vec3 symbolColor = vec3(1.0);
if (u_colorCount > 0) {
int colorIdx = int(mod(fi + floor(t * 0.1), float(u_colorCount)));
symbolColor = u_colors[colorIdx];
}
// Interaction: Laser impact
symbolColor = mix(symbolColor, vec3(1.0), laserHit * 0.8);
float finalGlow = (0.5 + beat * 0.5 + laserHit * 2.0);
finalColor = mix(finalColor, symbolColor, intensity * clamp(0.7 + beat * 0.3 + laserHit, 0.0, 1.0));
// Add a slight glow/aura
finalColor += symbolColor * (1.0 - smoothstep(0.0, size * 2.5, abs(d))) * 0.15 * finalGlow;
}
// --- Draw Laser Beams ---
for (int i = 0; i < 3; i++) {
float distToBeam = distToSegment(distortedP, lp1[i], lp2[i]);
float beamWidth = 0.008 + beat * 0.005;
float beamIntensity = smoothstep(beamWidth, 0.0, distToBeam);
vec3 beamColor = vec3(1.0);
if (u_colorCount > 0) {
int colorIdx = int(mod(float(i) + floor(t * 0.2), float(u_colorCount)));
beamColor = u_colors[colorIdx];
}
finalColor += beamColor * beamIntensity * (0.6 + beat * 0.4);
}
gl_FragColor = vec4(finalColor, mask * u_opacity);
}
`;
export class DruggedOutFlowVisualizer extends VisualizerInterface {
getName() {
return "Drugged Out Flow";
}
getFragmentShader() {
return fragmentShader;
}
}

View File

@ -2,12 +2,14 @@ import { ClassicVisualizer } from './ClassicVisualizer.js';
import { NebulaVisualizer } from './NebulaVisualizer.js'; import { NebulaVisualizer } from './NebulaVisualizer.js';
import { FloatingShapesVisualizer } from './FloatingShapesVisualizer.js'; import { FloatingShapesVisualizer } from './FloatingShapesVisualizer.js';
import { SynthwaveRunVisualizer } from './SynthwaveRunVisualizer.js'; import { SynthwaveRunVisualizer } from './SynthwaveRunVisualizer.js';
import { DruggedOutFlowVisualizer } from './DruggedOutFlowVisualizer.js';
export const visualizerLibrary = [ export const visualizerLibrary = [
new ClassicVisualizer(), new ClassicVisualizer(),
new NebulaVisualizer(), new NebulaVisualizer(),
new FloatingShapesVisualizer(), new FloatingShapesVisualizer(),
new SynthwaveRunVisualizer(), new SynthwaveRunVisualizer(),
new DruggedOutFlowVisualizer(),
]; ];
export default visualizerLibrary; export default visualizerLibrary;