Files
pivoine.art/assets/js/visualizer/base-visualizer.js
Sebastian Krüger e50c8a2503 feat(visualizer): add multiple visualizers with mouse tracking and beat response
- Add BaseVisualizer class for shared visualizer logic
- Add Helix visualizer (DNA helix with rungs, beat compression/bounce)
- Add Tunnel visualizer (ring tunnel with depth parallax)
- Refactor SphereVisualizer to extend BaseVisualizer
- Add mouse tracking to all visualizers (canvas-relative, centered)
- Add visualizer switching via logo click (persisted to localStorage)
- Add beat-responsive bouncing and compression effects

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 21:11:38 +01:00

91 lines
1.8 KiB
JavaScript

/**
* Base Visualizer Class
* Abstract base for all audio visualizers
*/
import * as THREE from 'three';
export class BaseVisualizer {
constructor(scene) {
this.scene = scene;
this.mesh = null;
this.isVisible = false;
}
/**
* Initialize geometry, materials, and mesh
* Override in subclasses
*/
init() {
throw new Error('init() must be implemented by subclass');
}
/**
* Update visualizer with audio data
* @param {Object} bands - { low, mid, high } frequency bands (0-1)
* @param {number} time - Animation time
*/
update(bands, time) {
throw new Error('update() must be implemented by subclass');
}
/**
* Add visualizer to scene
*/
show() {
if (this.mesh && !this.isVisible) {
this.scene.add(this.mesh);
this.isVisible = true;
}
}
/**
* Remove visualizer from scene
*/
hide() {
if (this.mesh && this.isVisible) {
this.scene.remove(this.mesh);
this.isVisible = false;
}
}
/**
* Cleanup resources
*/
dispose() {
this.hide();
if (this.mesh) {
this.mesh.geometry?.dispose();
if (this.mesh.material) {
if (Array.isArray(this.mesh.material)) {
this.mesh.material.forEach(m => m.dispose());
} else {
this.mesh.material.dispose();
}
}
}
}
/**
* Helper: Create shader material with common settings
*/
createShaderMaterial(vertexShader, fragmentShader, uniforms = {}) {
return new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms: {
uTime: { value: 0 },
uLow: { value: 0 },
uMid: { value: 0 },
uHigh: { value: 0 },
...uniforms
},
transparent: true,
blending: THREE.AdditiveBlending,
depthWrite: false
});
}
}
export default BaseVisualizer;