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>
This commit is contained in:
133
assets/js/visualizer/starfield.js
Normal file
133
assets/js/visualizer/starfield.js
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Starfield Visualizer
|
||||
* Flying through stars that streak and pulse with the beat
|
||||
*/
|
||||
|
||||
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 aSize;
|
||||
attribute float aSpeed;
|
||||
|
||||
varying float vAlpha;
|
||||
varying float vStreak;
|
||||
|
||||
void main() {
|
||||
vec3 pos = position;
|
||||
|
||||
// Stars fly toward camera (positive Z)
|
||||
float speed = (10.0 + uLow * 30.0) * aSpeed;
|
||||
float z = mod(pos.z + uTime * speed, 200.0) - 100.0;
|
||||
pos.z = z;
|
||||
|
||||
// Depth factor - stars closer are brighter and larger
|
||||
float depthFactor = (z + 100.0) / 200.0;
|
||||
|
||||
// Slight drift based on position
|
||||
pos.x += sin(uTime * 0.5 + position.x * 0.1) * 2.0;
|
||||
pos.y += cos(uTime * 0.5 + position.y * 0.1) * 2.0;
|
||||
|
||||
// Mouse parallax - closer stars move more
|
||||
pos.x -= uMouseX * 10.0 * (1.0 - depthFactor);
|
||||
pos.y -= uMouseY * 8.0 * (1.0 - depthFactor);
|
||||
|
||||
vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
|
||||
|
||||
// Size increases as stars approach
|
||||
float size = aSize * (1.0 - depthFactor) * 3.0;
|
||||
size *= (1.0 + uLow * 0.5);
|
||||
gl_PointSize = size * (300.0 / -mvPosition.z);
|
||||
|
||||
// Alpha and streak based on depth and speed
|
||||
vAlpha = (1.0 - depthFactor) * 0.9 + uHigh * 0.2;
|
||||
vStreak = speed * 0.02; // For potential streak effect
|
||||
|
||||
gl_Position = projectionMatrix * mvPosition;
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentShader = `
|
||||
varying float vAlpha;
|
||||
varying float vStreak;
|
||||
|
||||
void main() {
|
||||
vec2 uv = gl_PointCoord - vec2(0.5);
|
||||
float dist = length(uv);
|
||||
|
||||
if (dist > 0.5) discard;
|
||||
|
||||
// Core glow
|
||||
float alpha = 1.0 - smoothstep(0.0, 0.5, dist);
|
||||
alpha *= vAlpha;
|
||||
|
||||
// Add slight elongation effect for speed
|
||||
float streak = 1.0 - smoothstep(0.0, 0.3, abs(uv.y));
|
||||
alpha *= mix(1.0, streak, min(vStreak, 0.5));
|
||||
|
||||
vec3 color = vec3(1.0);
|
||||
|
||||
gl_FragColor = vec4(color, alpha);
|
||||
}
|
||||
`;
|
||||
|
||||
export class StarfieldVisualizer extends BaseVisualizer {
|
||||
static name = 'Starfield';
|
||||
|
||||
constructor(scene) {
|
||||
super(scene);
|
||||
this.starCount = 2000;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
const positions = new Float32Array(this.starCount * 3);
|
||||
const sizes = new Float32Array(this.starCount);
|
||||
const speeds = new Float32Array(this.starCount);
|
||||
|
||||
for (let i = 0; i < this.starCount; i++) {
|
||||
// Distribute stars in a cylinder around the camera
|
||||
const angle = Math.random() * Math.PI * 2;
|
||||
const radius = 10 + Math.random() * 60;
|
||||
|
||||
positions[i * 3] = Math.cos(angle) * radius;
|
||||
positions[i * 3 + 1] = Math.sin(angle) * radius;
|
||||
positions[i * 3 + 2] = Math.random() * 200 - 100; // -100 to 100
|
||||
|
||||
sizes[i] = 1.0 + Math.random() * 3.0;
|
||||
speeds[i] = 0.5 + Math.random() * 1.0;
|
||||
}
|
||||
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||
geometry.setAttribute('aSize', new THREE.BufferAttribute(sizes, 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 StarfieldVisualizer;
|
||||
Reference in New Issue
Block a user