- 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>
135 lines
3.4 KiB
JavaScript
135 lines
3.4 KiB
JavaScript
/**
|
|
* Wave Grid Visualizer
|
|
* 3D plane of dots that ripple like water
|
|
*/
|
|
|
|
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 aIndex;
|
|
|
|
varying float vAlpha;
|
|
varying float vHeight;
|
|
|
|
void main() {
|
|
vec3 pos = position;
|
|
|
|
// Distance from center for wave calculations
|
|
float dist = length(pos.xy);
|
|
|
|
// Multiple wave sources
|
|
float wave1 = sin(dist * 0.3 - uTime * 2.0) * (5.0 + uLow * 10.0);
|
|
float wave2 = sin(dist * 0.5 - uTime * 3.0 + 1.0) * (3.0 + uMid * 5.0);
|
|
float wave3 = cos(pos.x * 0.2 + uTime) * cos(pos.y * 0.2 + uTime) * (2.0 + uHigh * 4.0);
|
|
|
|
// Combine waves for height
|
|
float height = wave1 + wave2 + wave3;
|
|
pos.z = height;
|
|
|
|
// Mouse creates ripple effect
|
|
float mouseDist = length(pos.xy - vec2(uMouseX * 30.0, uMouseY * 30.0));
|
|
pos.z += sin(mouseDist * 0.5 - uTime * 4.0) * 3.0 * (1.0 / (1.0 + mouseDist * 0.1));
|
|
|
|
// Rotate grid for better viewing angle
|
|
float tiltAngle = 0.6;
|
|
float cosT = cos(tiltAngle);
|
|
float sinT = sin(tiltAngle);
|
|
float newY = pos.y * cosT - pos.z * sinT;
|
|
float newZ = pos.y * sinT + pos.z * cosT;
|
|
pos.y = newY;
|
|
pos.z = newZ - 20.0; // Push back
|
|
|
|
vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
|
|
|
|
// Size pulses with bass
|
|
float size = 2.5 + uLow * 1.5;
|
|
gl_PointSize = size * (300.0 / -mvPosition.z);
|
|
|
|
// Alpha based on height
|
|
vAlpha = 0.6 + abs(height) * 0.02;
|
|
vHeight = height;
|
|
|
|
gl_Position = projectionMatrix * mvPosition;
|
|
}
|
|
`;
|
|
|
|
const fragmentShader = `
|
|
varying float vAlpha;
|
|
varying float vHeight;
|
|
|
|
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 GridVisualizer extends BaseVisualizer {
|
|
static name = 'Grid';
|
|
|
|
constructor(scene) {
|
|
super(scene);
|
|
this.gridSize = 40; // 40x40 grid
|
|
this.spacing = 3;
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
const count = this.gridSize * this.gridSize;
|
|
const positions = new Float32Array(count * 3);
|
|
const indices = new Float32Array(count);
|
|
|
|
let idx = 0;
|
|
const offset = (this.gridSize - 1) * this.spacing / 2;
|
|
|
|
for (let x = 0; x < this.gridSize; x++) {
|
|
for (let y = 0; y < this.gridSize; y++) {
|
|
positions[idx * 3] = x * this.spacing - offset;
|
|
positions[idx * 3 + 1] = y * this.spacing - offset;
|
|
positions[idx * 3 + 2] = 0;
|
|
|
|
indices[idx] = idx;
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
const geometry = new THREE.BufferGeometry();
|
|
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
|
geometry.setAttribute('aIndex', new THREE.BufferAttribute(indices, 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 GridVisualizer;
|