// Import Three.js library (already included in index.html) // const THREE = window.THREE; class Background { constructor() { // Initialize core Three.js components this.scene = new THREE.Scene(); this.camera = new THREE.PerspectiveCamera( 75, // Field of view window.innerWidth / window.innerHeight, // Aspect ratio 0.1, // Near plane 1000 // Far plane ); // Setup renderer with transparency and anti-aliasing this.renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('#webgl-background'), alpha: true, antialias: true }); this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setClearColor(0x0a0a0a, 1); // Dark background color // Initialize particle system properties this.particles = []; this.particleCount = 100; this.particleGeometry = new THREE.BufferGeometry(); this.particleMaterial = new THREE.PointsMaterial({ size: 2, color: 0xffffff, transparent: true, opacity: 0.5, blending: THREE.AdditiveBlending }); // Set up camera position this.camera.position.z = 100; // Initialize the background this.init(); // Bind event listeners this.bindEvents(); } init() { // Create particle positions array const positions = new Float32Array(this.particleCount * 3); // Generate random positions for particles for (let i = 0; i < this.particleCount; i++) { const i3 = i * 3; // Index for x, y, z coordinates positions[i3] = (Math.random() - 0.5) * window.innerWidth; // X coordinate positions[i3 + 1] = (Math.random() - 0.5) * window.innerHeight; // Y coordinate positions[i3 + 2] = (Math.random() - 0.5) * 500; // Z coordinate // Store particle data for animation this.particles.push({ velocity: (Math.random() - 0.5) * 0.2, baseX: positions[i3], baseY: positions[i3 + 1] }); } // Set particle positions in geometry this.particleGeometry.setAttribute( 'position', new THREE.BufferAttribute(positions, 3) ); // Create particle system and add to scene this.particleSystem = new THREE.Points( this.particleGeometry, this.particleMaterial ); this.scene.add(this.particleSystem); // Start animation loop this.animate(); } // Handle window resize events bindEvents() { window.addEventListener('resize', () => { // Update camera aspect ratio and projection matrix this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); // Update renderer size this.renderer.setSize(window.innerWidth, window.innerHeight); }); } // Animation loop for particle movement animate() { requestAnimationFrame(() => this.animate()); const positions = this.particleGeometry.attributes.position.array; const time = Date.now() * 0.0005; // Update particle positions with wave-like motion for (let i = 0; i < this.particleCount; i++) { const i3 = i * 3; const particle = this.particles[i]; // Create wave-like motion using sine waves positions[i3] = particle.baseX + Math.sin(time + i) * 2; positions[i3 + 1] = particle.baseY + Math.cos(time + i) * 2; // Add slight drift to z-position positions[i3 + 2] += particle.velocity; // Reset particles that drift too far if (Math.abs(positions[i3 + 2]) > 250) { positions[i3 + 2] = -250; } } // Mark particle positions for update this.particleGeometry.attributes.position.needsUpdate = true; // Add subtle camera movement this.camera.position.x = Math.sin(time) * 10; this.camera.position.y = Math.cos(time) * 10; this.camera.lookAt(this.scene.position); // Render the scene this.renderer.render(this.scene, this.camera); } // Method to add dramatic effects during game events addDramaticEffect(type) { switch(type) { case 'impostor_reveal': // Create a dramatic red flash effect this.particleMaterial.color.setHex(0xff0000); setTimeout(() => { this.particleMaterial.color.setHex(0xffffff); }, 1000); break; case 'round_start': // Increase particle movement temporarily const originalVelocities = this.particles.map(p => p.velocity); this.particles.forEach(p => p.velocity *= 2); setTimeout(() => { this.particles.forEach((p, i) => p.velocity = originalVelocities[i]); }, 2000); break; case 'voting': // Create a pulsing effect const pulseAnimation = () => { this.particleMaterial.size = 2 + Math.sin(Date.now() * 0.005) * 1; }; const pulseInterval = setInterval(pulseAnimation, 16); setTimeout(() => { clearInterval(pulseInterval); this.particleMaterial.size = 2; }, 3000); break; } } } // Initialize the background when the DOM is loaded document.addEventListener('DOMContentLoaded', () => { const background = new Background(); // Expose background instance for game events window.gameBackground = background; }); // Export the Background class for potential module usage export default Background;