// The Conscious Being - Main Controller import { Renderer } from './Renderer.js'; import { ParticleSystem } from './ParticleSystem.js'; import { FlockingBehavior, BreathingBehavior, FollowBehavior } from './Behaviors.js'; export class Being { constructor(canvas) { this.canvas = canvas; this.renderer = null; this.particleSystem = null; this.behaviors = []; this.mousePosition = { x: 0.5, y: 0.5 }; // Normalized 0-1 this.target = null; this.mode = 'idle'; // idle, aware, engaged, background this.isRunning = false; this.lastTime = 0; this.initialized = false; // Store bound handlers for cleanup this._resizeHandler = this.handleResize.bind(this); this.init(); } init() { try { // Initialize renderer with WebGL error handling this.renderer = new Renderer(this.canvas); // Initialize particle system this.particleSystem = new ParticleSystem(800); // Initialize behaviors this.behaviors = [ new BreathingBehavior(), new FlockingBehavior(), new FollowBehavior() ]; // Add particle system to scene this.renderer.add(this.particleSystem.mesh); // Handle resize window.addEventListener('resize', this._resizeHandler); this.initialized = true; } catch (error) { console.error('[Being] WebGL initialization failed:', error); this.showFallback(); } } showFallback() { // Show CSS-based fallback animation when WebGL is unavailable if (this.canvas) { this.canvas.style.display = 'none'; const fallback = document.createElement('div'); fallback.className = 'conscious-fallback'; fallback.innerHTML = `
`; this.canvas.parentNode?.insertBefore(fallback, this.canvas); } } start() { if (this.isRunning || !this.initialized) return; this.isRunning = true; this.lastTime = performance.now(); this.animate(); console.log('[Being] Started'); } stop() { this.isRunning = false; console.log('[Being] Stopped'); } animate() { if (!this.isRunning) return; const currentTime = performance.now(); const deltaTime = (currentTime - this.lastTime) / 1000; // Convert to seconds this.lastTime = currentTime; this.update(deltaTime, currentTime); this.renderer.render(); requestAnimationFrame(() => this.animate()); } update(deltaTime, time) { // Apply behaviors based on mode const params = { time, deltaTime, mousePosition: this.mousePosition, target: this.target, mode: this.mode }; // Apply each behavior this.behaviors.forEach(behavior => { behavior.apply(this.particleSystem, params); }); // Update particle system this.particleSystem.update(deltaTime); // Update uniforms this.particleSystem.material.uniforms.uTime.value = time * 0.001; this.particleSystem.material.uniforms.uMouse.value.set( this.mousePosition.x, this.mousePosition.y ); } updateMousePosition(x, y) { // Normalize to 0-1 this.mousePosition = { x: x / window.innerWidth, y: 1 - (y / window.innerHeight) // Flip Y for WebGL }; // Update mode based on mouse activity if (this.mode === 'idle') { this.setMode('aware'); } } setTarget(element) { if (!element) { this.target = null; return; } const rect = element.getBoundingClientRect(); this.target = { x: (rect.left + rect.width / 2) / window.innerWidth, y: 1 - ((rect.top + rect.height / 2) / window.innerHeight), width: rect.width / window.innerWidth, height: rect.height / window.innerHeight }; this.setMode('engaged'); } clearTarget() { this.target = null; if (this.mode === 'engaged') { this.setMode('aware'); } } setMode(mode) { if (this.mode === mode) return; const oldMode = this.mode; this.mode = mode; console.log(`[Being] Mode: ${oldMode} -> ${mode}`); // Adjust behaviors based on mode this.behaviors.forEach(behavior => { behavior.onModeChange(mode, oldMode); }); // Adjust particle appearance if (mode === 'background') { this.particleSystem.setOpacity(0.3); } else { this.particleSystem.setOpacity(1.0); } } handleResize() { this.renderer.handleResize(); } dispose() { this.stop(); // Remove event listeners window.removeEventListener('resize', this._resizeHandler); // Dispose of WebGL resources if (this.renderer) { this.renderer.dispose(); } if (this.particleSystem) { this.particleSystem.dispose(); } this.initialized = false; } } export default Being;