Files
pivoine.art/assets/js/visualizer/scene.js
2025-11-29 17:51:00 +01:00

111 lines
2.4 KiB
JavaScript

/**
* WebGL Visualizer Scene
* Three.js particle system that reacts to audio frequency data
*/
import * as THREE from 'three';
import { ParticleSystem } from './particles.js';
export class Visualizer {
constructor(canvas, audioManager) {
this.canvas = canvas;
this.audioManager = audioManager;
this.running = false;
this.time = 0;
if (!canvas) {
console.warn('Visualizer: No canvas provided');
return;
}
this.init();
}
init() {
// Scene setup
this.scene = new THREE.Scene();
// Camera
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.z = 50;
// Renderer
this.renderer = new THREE.WebGLRenderer({
canvas: this.canvas,
alpha: true,
antialias: true,
powerPreference: 'high-performance'
});
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
// Particles
this.particles = new ParticleSystem(5000);
this.scene.add(this.particles.mesh);
// Event listeners
window.addEventListener('resize', () => this.resize());
// Start animation
this.start();
}
resize() {
if (!this.camera || !this.renderer) return;
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
start() {
if (this.running) return;
this.running = true;
this.animate();
}
pause() {
this.running = false;
}
resume() {
this.start();
}
animate() {
if (!this.running) return;
requestAnimationFrame(() => this.animate());
this.time += 0.01;
// Get audio frequency bands
let bands = { low: 0, mid: 0, high: 0 };
if (this.audioManager?.isInitialized) {
bands = this.audioManager.getFrequencyBands();
}
// Update particles with audio data
this.particles.update(bands, this.time);
// Subtle camera movement
this.camera.position.x = Math.sin(this.time * 0.1) * 3;
this.camera.position.y = Math.cos(this.time * 0.15) * 2;
this.camera.lookAt(0, 0, 0);
this.renderer.render(this.scene, this.camera);
}
destroy() {
this.running = false;
this.particles?.dispose();
this.renderer?.dispose();
}
}
export default Visualizer;