function updateCamera() { const globalTime = Date.now() * 0.00005; const lookAtTime = Date.now() * 0.00003; const camAmplitude = 0.7; const lookAmplitude = 0.05; // Base Camera Position in front of the TV const baseX = -0.5; const baseY = 1.5; const baseZ = 2.5; // Base LookAt target (Center of the screen) const baseTargetX = -0.7; const baseTargetY = 1.7; const baseTargetZ = -0.3; // Camera Position Offsets (Drift) const camOffsetX = Math.sin(globalTime * 3.1) * camAmplitude; const camOffsetY = Math.cos(globalTime * 2.5) * camAmplitude * 0.4; const camOffsetZ = Math.cos(globalTime * 3.2) * camAmplitude * 1.4; camera.position.x = baseX + camOffsetX; camera.position.y = baseY + camOffsetY; camera.position.z = baseZ + camOffsetZ; // LookAt Target Offsets (Subtle Gaze Shift) const lookOffsetX = Math.sin(lookAtTime * 1.5) * lookAmplitude; const lookOffsetY = Math.cos(lookAtTime * 1.2) * lookAmplitude; // Apply lookAt to the subtly shifted target camera.lookAt(baseTargetX + lookOffsetX, baseTargetY + lookOffsetY, baseTargetZ); } function updateLampFlicker() { const flickerChance = 0.995; const restoreRate = 0.15; if (Math.random() > flickerChance) { // Flickers quickly to a dimmer random value (between 0.3 and 1.05) let lampLightIntensity = originalLampIntensity * (0.3 + Math.random() * 0.7); lampLightSpot.intensity = lampLightIntensity; lampLightPoint.intensity = lampLightIntensity; } else if (lampLightPoint.intensity < originalLampIntensity) { // Smoothly restore original intensity let lampLightIntensity = THREE.MathUtils.lerp(lampLightPoint.intensity, originalLampIntensity, restoreRate); lampLightSpot.intensity = lampLightIntensity; lampLightPoint.intensity = lampLightIntensity; } } function updateScreenLight() { if (isVideoLoaded && screenLight.intensity > 0) { const pulseTarget = originalScreenIntensity + (Math.random() - 0.5) * screenIntensityPulse; screenLight.intensity = THREE.MathUtils.lerp(screenLight.intensity, pulseTarget, 0.1); const lightTime = Date.now() * 0.0001; const radius = 0.01; const centerX = 0; const centerY = 1.5; screenLight.position.x = centerX + Math.cos(lightTime) * radius; screenLight.position.y = centerY + Math.sin(lightTime * 1.5) * radius * 0.5; // Slightly different freq for Y } } function updateVideo() { if (videoTexture) { videoTexture.needsUpdate = true; } } function updateVcr() { const currentTime = baseTime + videoElement.currentTime; if (Math.abs(currentTime - lastUpdateTime) > 0.1) { updateVcrDisplay(currentTime); lastUpdateTime = currentTime; } if (currentTime - lastBlinkToggleTime > 0.5) { // Blink every 0.5 seconds blinkState = !blinkState; lastBlinkToggleTime = currentTime; } } // --- Animation Loop --- function animate() { requestAnimationFrame(animate); effectsManager.update(); updateCamera(); updateLampFlicker(); updateScreenLight(); updateVideo(); updateVcr(); // RENDER! renderer.render(scene, camera); } // --- Window Resize Handler --- function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }