Files
Sebastian Krüger 8d9d47cea7 feat: add 6 new audio visualizers
- Vortex: spiral particles pulled toward center, speed reacts to beat
- Starfield: flying through stars with depth parallax and streak effects
- Grid: 3D wave plane with ripple effects from mouse and audio
- Galaxy: 3-arm spiral galaxy with tilted perspective
- Waveform: circular audio waveform in concentric rings
- Kaleidoscope: 8-segment mirrored geometric patterns

All visualizers include:
- GLSL shaders with audio reactivity (low/mid/high frequencies)
- Mouse tracking for interactive parallax
- Beat-synchronized animations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 10:10:54 +01:00

139 lines
3.7 KiB
JavaScript

/**
* Vortex Visualizer
* Spiral of particles being pulled into a center point
*/
import * as THREE from 'three';
import { BaseVisualizer } from './base-visualizer.js';
const vertexShader = `
uniform float uTime;
uniform float uLow;
uniform float uMid;
uniform float uHigh;
uniform float uMouseX;
uniform float uMouseY;
attribute float aAngle;
attribute float aRadius;
attribute float aSpeed;
varying float vAlpha;
varying float vRadius;
void main() {
// Spiral motion - particles orbit and get pulled inward
float time = uTime * (0.5 + uMid * 2.0);
float angle = aAngle + time * aSpeed;
// Radius pulses with bass, particles get pulled in
float radiusPull = 1.0 - uLow * 0.3;
float radius = aRadius * radiusPull;
// Add some vertical motion based on radius
float z = sin(angle * 3.0 + uTime) * 5.0 * (1.0 - aRadius / 50.0);
z += uLow * 10.0; // Bounce toward viewer
// Calculate position
float x = cos(angle) * radius;
float y = sin(angle) * radius;
// High frequencies add turbulence
x += sin(aAngle * 10.0 + uTime * 5.0) * uHigh * 3.0;
y += cos(aAngle * 10.0 + uTime * 5.0) * uHigh * 3.0;
// Mouse offset
x -= uMouseX * 5.0;
y -= uMouseY * 4.0;
vec3 pos = vec3(x, y, z);
vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
// Size based on radius - larger in center
float size = 3.0 * (1.0 - aRadius / 60.0) + uLow * 2.0;
gl_PointSize = size * (300.0 / -mvPosition.z);
// Alpha - brighter toward center
vAlpha = 0.8 - aRadius / 80.0 + uLow * 0.2;
vRadius = aRadius;
gl_Position = projectionMatrix * mvPosition;
}
`;
const fragmentShader = `
varying float vAlpha;
varying float vRadius;
void main() {
float dist = length(gl_PointCoord - vec2(0.5));
if (dist > 0.5) discard;
float alpha = 1.0 - smoothstep(0.1, 0.5, dist);
alpha *= vAlpha;
// Pure white
vec3 color = vec3(1.0);
gl_FragColor = vec4(color, alpha);
}
`;
export class VortexVisualizer extends BaseVisualizer {
static name = 'Vortex';
constructor(scene) {
super(scene);
this.particleCount = 3000;
this.init();
}
init() {
const positions = new Float32Array(this.particleCount * 3);
const angles = new Float32Array(this.particleCount);
const radii = new Float32Array(this.particleCount);
const speeds = new Float32Array(this.particleCount);
for (let i = 0; i < this.particleCount; i++) {
// Distribute particles in a disk
const angle = Math.random() * Math.PI * 2;
const radius = 5 + Math.random() * 45; // 5 to 50
positions[i * 3] = 0;
positions[i * 3 + 1] = 0;
positions[i * 3 + 2] = 0;
angles[i] = angle;
radii[i] = radius;
// Faster rotation for particles closer to center
speeds[i] = 0.5 + (1.0 - radius / 50.0) * 1.5;
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('aAngle', new THREE.BufferAttribute(angles, 1));
geometry.setAttribute('aRadius', new THREE.BufferAttribute(radii, 1));
geometry.setAttribute('aSpeed', new THREE.BufferAttribute(speeds, 1));
this.material = this.createShaderMaterial(vertexShader, fragmentShader, {
uMouseX: { value: 0 },
uMouseY: { value: 0 }
});
this.mesh = new THREE.Points(geometry, this.material);
}
update(bands, time, mouse = { x: 0, y: 0 }) {
if (!this.material) return;
this.material.uniforms.uTime.value = time;
this.material.uniforms.uLow.value = bands.low || 0;
this.material.uniforms.uMid.value = bands.mid || 0;
this.material.uniforms.uHigh.value = bands.high || 0;
this.material.uniforms.uMouseX.value = mouse.x;
this.material.uniforms.uMouseY.value = mouse.y;
}
}
export default VortexVisualizer;