- 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>
142 lines
3.6 KiB
JavaScript
142 lines
3.6 KiB
JavaScript
/**
|
|
* Waveform Circle Visualizer
|
|
* Audio waveform wrapped in a circle - classic visualizer style
|
|
*/
|
|
|
|
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 aRing;
|
|
|
|
varying float vAlpha;
|
|
varying float vRing;
|
|
|
|
void main() {
|
|
// Base radius for each ring
|
|
float baseRadius = 15.0 + aRing * 12.0;
|
|
|
|
// Waveform displacement based on angle and audio
|
|
float waveFreq = 8.0 + aRing * 4.0;
|
|
float wave = sin(aAngle * waveFreq + uTime * 3.0) * (uMid * 8.0 + 2.0);
|
|
wave += cos(aAngle * waveFreq * 0.5 - uTime * 2.0) * (uLow * 6.0);
|
|
wave += sin(aAngle * waveFreq * 2.0 + uTime * 5.0) * (uHigh * 4.0);
|
|
|
|
float radius = baseRadius + wave;
|
|
|
|
// Calculate position
|
|
float x = cos(aAngle) * radius;
|
|
float y = sin(aAngle) * radius;
|
|
float z = sin(aAngle * 3.0 + uTime) * 3.0 + uLow * 8.0;
|
|
|
|
// Slow rotation
|
|
float rotAngle = uTime * 0.1;
|
|
float cosR = cos(rotAngle);
|
|
float sinR = sin(rotAngle);
|
|
float newX = x * cosR - y * sinR;
|
|
float newY = x * sinR + y * cosR;
|
|
x = newX;
|
|
y = newY;
|
|
|
|
// 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 audio intensity
|
|
float size = 2.5 + uLow * 1.5 + abs(wave) * 0.1;
|
|
gl_PointSize = size * (300.0 / -mvPosition.z);
|
|
|
|
// Alpha - outer rings slightly dimmer
|
|
vAlpha = 0.8 - aRing * 0.15 + uLow * 0.2;
|
|
vRing = aRing;
|
|
|
|
gl_Position = projectionMatrix * mvPosition;
|
|
}
|
|
`;
|
|
|
|
const fragmentShader = `
|
|
varying float vAlpha;
|
|
varying float vRing;
|
|
|
|
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;
|
|
|
|
vec3 color = vec3(1.0);
|
|
|
|
gl_FragColor = vec4(color, alpha);
|
|
}
|
|
`;
|
|
|
|
export class WaveformVisualizer extends BaseVisualizer {
|
|
static name = 'Waveform';
|
|
|
|
constructor(scene) {
|
|
super(scene);
|
|
this.rings = 4;
|
|
this.pointsPerRing = 200;
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
const count = this.rings * this.pointsPerRing;
|
|
const positions = new Float32Array(count * 3);
|
|
const angles = new Float32Array(count);
|
|
const rings = new Float32Array(count);
|
|
|
|
let idx = 0;
|
|
for (let ring = 0; ring < this.rings; ring++) {
|
|
for (let i = 0; i < this.pointsPerRing; i++) {
|
|
const angle = (i / this.pointsPerRing) * Math.PI * 2;
|
|
|
|
positions[idx * 3] = 0;
|
|
positions[idx * 3 + 1] = 0;
|
|
positions[idx * 3 + 2] = 0;
|
|
|
|
angles[idx] = angle;
|
|
rings[idx] = ring;
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
const geometry = new THREE.BufferGeometry();
|
|
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
|
geometry.setAttribute('aAngle', new THREE.BufferAttribute(angles, 1));
|
|
geometry.setAttribute('aRing', new THREE.BufferAttribute(rings, 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 WaveformVisualizer;
|