// Galaxy background - moonlit terrain with floating stars // Includes FPS monitoring and fallback to simple background (function initGalaxy(){ console.clear(); // FPS monitoring let lastTime = performance.now(); let frameCount = 0; let fps = 60; let lowFpsCount = 0; const FPS_THRESHOLD = 27; const LOW_FPS_SAMPLES = 5; // Switch after 5 consecutive low FPS readings let isSimpleMode = false; var ww = window.innerWidth, wh = window.innerHeight; var renderer = new THREE.WebGLRenderer({ antialias: true, canvas: document.querySelector('#galaxy-canvas') }); renderer.setSize(ww, wh); renderer.setClearColor(0x001a2d); var scene = new THREE.Scene(); scene.fog = new THREE.Fog(0x001a2d, 80, 140); var camera = new THREE.PerspectiveCamera(45, ww/wh, 0.1, 200); camera.position.x = 70; camera.position.y = 30; camera.position.z = 5; camera.lookAt(new THREE.Vector3()); /* LIGHTS */ var moonLight = new THREE.PointLight(0xffffff, 2, 150); scene.add(moonLight); var moon; function createMoon() { var geometry = new THREE.SphereGeometry(8, 32, 32); var material = new THREE.MeshPhongMaterial({ color: 0x26fdd9, shininess: 15, emissive: 0x2bb2e6, emissiveIntensity: 0.8 }); moon = new THREE.Mesh(geometry, material); moon.position.x = -9; moon.position.z = -6.5; moon.position.y = 1; moon.rotation.y = -1; scene.add(moon); moonLight.position.copy(moon.position); moonLight.position.y += 4; var moonLight2 = new THREE.PointLight(0xffffff, 0.6, 150); scene.add(moonLight2); moonLight2.position.x += 20; moonLight2.position.y -= 20; moonLight2.position.z -= 25; } // Initialize simplex noise var noise = new SimplexNoise(); function createTerrain() { var geometry = new THREE.PlaneGeometry(150, 150, 120, 120); var m = new THREE.Matrix4(); m.makeRotationX(Math.PI * -0.5); geometry.applyMatrix4(m); var positions = geometry.attributes.position; for(var i = 0; i < positions.count; i++) { var x = positions.getX(i); var z = positions.getZ(i); var ratio = noise.noise3D(x * 0.03, z * 0.03, 0); positions.setY(i, ratio * 10); } positions.needsUpdate = true; geometry.computeVertexNormals(); var material = new THREE.MeshPhongMaterial({ color: 0x198257, emissive: 0x032f50 }); var plane = new THREE.Mesh(geometry, material); scene.add(plane); } var stars = new THREE.Group(); scene.add(stars); var starsLights = new THREE.Group(); scene.add(starsLights); var starsAmount = 20; function createStars() { var geometry = new THREE.SphereGeometry(0.3, 16, 16); var material = new THREE.MeshBasicMaterial({color: 0xffffff}); for(var i = 0; i < starsAmount; i++) { var star = new THREE.Mesh(geometry, material); star.position.x = (Math.random() - 0.5) * 150; star.position.z = (Math.random() - 0.5) * 150; var ratio = noise.noise3D(star.position.x * 0.03, star.position.z * 0.03, 0); star.position.y = ratio * 10 + 0.3; stars.add(star); var velX = (Math.random() + 0.1) * 0.1 * (Math.random() < 0.5 ? -1 : 1); var velY = (Math.random() + 0.1) * 0.1 * (Math.random() < 0.5 ? -1 : 1); star.vel = new THREE.Vector2(velX, velY); var starLight = new THREE.PointLight(0xffffff, 0.8, 3); starLight.position.copy(star.position); starLight.position.y += 0.5; starsLights.add(starLight); } } function updateStar(star, index) { if(star.position.x < -75) { star.position.x = 75; } if(star.position.x > 75) { star.position.x = -75; } if(star.position.z < -75) { star.position.z = 75; } if(star.position.z > 75) { star.position.z = -75; } star.position.x += star.vel.x; star.position.z += star.vel.y; var ratio = noise.noise3D(star.position.x * 0.03, star.position.z * 0.03, 0); star.position.y = ratio * 10 + 0.3; starsLights.children[index].position.copy(star.position); starsLights.children[index].position.y += 0.5; } function render(a) { requestAnimationFrame(render); if (!isSimpleMode) { // FPS monitoring (only when not in simple mode) frameCount++; const currentTime = performance.now(); const delta = currentTime - lastTime; if (delta >= 500) { // Update FPS every 500ms fps = Math.round((frameCount * 1000) / delta); console.log(`FPS: ${fps} (threshold: ${FPS_THRESHOLD}, low samples: ${lowFpsCount}/${LOW_FPS_SAMPLES})`); frameCount = 0; lastTime = currentTime; // Check if FPS is consistently low if (fps < FPS_THRESHOLD) { lowFpsCount++; console.warn(`⚠️ Low FPS detected! ${fps} FPS (${lowFpsCount}/${LOW_FPS_SAMPLES})`); if (lowFpsCount >= LOW_FPS_SAMPLES) { console.warn(`🔄 Switching to simple background mode...`); switchToSimpleBackground(); isSimpleMode = true; // Stop monitoring - mode persists until page refresh } } else { lowFpsCount = 0; // Reset counter if FPS improves } } // Render 3D scene for(var i = 0; i < starsAmount; i++) { updateStar(stars.children[i], i); } renderer.render(scene, camera); } // If in simple mode, just keep the animation frame loop running but do nothing } function switchToSimpleBackground() { console.log('🎨 Activating simple background mode...'); // Stop rendering 3D scene const canvas = document.querySelector('#galaxy-canvas'); canvas.style.display = 'none'; console.log('✓ 3D canvas hidden'); // Create simple gradient background const overlay = document.querySelector('#background-overlay'); overlay.style.background = 'linear-gradient(180deg, #0a1628 0%, #001a2d 50%, #000d1a 100%)'; overlay.style.backdropFilter = 'none'; console.log('✓ Gradient background applied'); // Disable all animations and transitions disableAnimations(); console.log('✓ Animations disabled'); // Add simple animated stars createSimpleStars(); console.log('✓ Simple stars created'); console.log('✅ Simple background mode activated - animations disabled for performance'); } function disableAnimations() { // Create a style tag to disable animations const style = document.createElement('style'); style.id = 'low-fps-mode'; style.textContent = ` /* Disable all animations and transitions in low FPS mode */ *, *::before, *::after { animation-duration: 0s !important; animation-delay: 0s !important; transition-duration: 0s !important; transition-delay: 0s !important; } /* Remove hover effects and transforms */ .service-card:hover, .card:hover { transform: none !important; box-shadow: 0 4px 12px rgba(79,70,229,0.3) !important; } .control-btn:hover, .theme-toggle:hover, #ddg-search-btn:hover { transform: none !important; } /* Disable status dot animations */ .status-dot { animation: none !important; } /* Disable shimmer effects */ .service-card::before, .card::before { display: none !important; } /* Simplify search input focus */ #search-input:focus { box-shadow: 0 0 0 2px rgba(79,70,229,0.3) !important; } `; document.head.appendChild(style); } function createSimpleStars() { const starsContainer = document.createElement('div'); starsContainer.id = 'simple-stars'; starsContainer.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; `; // Create 50 simple star divs for (let i = 0; i < 50; i++) { const star = document.createElement('div'); star.style.cssText = ` position: absolute; width: 2px; height: 2px; background: white; border-radius: 50%; top: ${Math.random() * 100}%; left: ${Math.random() * 100}%; opacity: ${Math.random() * 0.5 + 0.3}; animation: twinkle ${Math.random() * 3 + 2}s infinite ease-in-out; `; starsContainer.appendChild(star); } // Add twinkle animation if (!document.querySelector('#simple-stars-style')) { const style = document.createElement('style'); style.id = 'simple-stars-style'; style.textContent = ` @keyframes twinkle { 0%, 100% { opacity: 0.3; } 50% { opacity: 1; } } `; document.head.appendChild(style); } document.body.appendChild(starsContainer); } function onResize() { ww = window.innerWidth; wh = window.innerHeight; camera.aspect = ww / wh; camera.updateProjectionMatrix(); renderer.setSize(ww, wh); } createMoon(); createTerrain(); createStars(); requestAnimationFrame(render); window.addEventListener('resize', onResize); })();