Help
RSS
API
Feed
Maltego
Contact
Domain > deegee-analytics.click
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2025-04-16
3.166.152.92
(
ClassC
)
2026-01-17
18.161.6.31
(
ClassC
)
Port 80
HTTP/1.1 301 Moved PermanentlyServer: CloudFrontDate: Sat, 17 Jan 2026 12:47:27 GMTContent-Type: text/htmlContent-Length: 167Connection: keep-aliveLocation: https://deegee-analytics.click/X-Cache: Redirect from cloudfrontVia: 1.1 c28e01aa413e9ea602538ccda1511062.cloudfront.net (CloudFront)X-Amz-Cf-Pop: HIO52-P1Alt-Svc: h3:443; ma86400X-Amz-Cf-Id: y0ad_FQYhqwsCEJq5oTuofYNyCc5piJfLY9WX8W4mmf7MmMyg_o__g html>head>title>301 Moved Permanently/title>/head>body>center>h1>301 Moved Permanently/h1>/center>hr>center>CloudFront/center>/body>/html>
Port 443
HTTP/1.1 200 OKContent-Type: text/htmlContent-Length: 86489Connection: keep-aliveLast-Modified: Tue, 15 Apr 2025 16:19:29 GMTx-amz-server-side-encryption: AES256Accept-Ranges: bytesServer: AmazonS3Date: Sat, 17 Jan 2026 12:47:28 GMTETag: 6d9e90e7e085d83299e254ddb3015497X-Cache: RefreshHit from cloudfrontVia: 1.1 4b800f7fa2c3fbb9f4f3c505b0df315e.cloudfront.net (CloudFront)X-Amz-Cf-Pop: HIO52-P1Alt-Svc: h3:443; ma86400X-Amz-Cf-Id: m2WDrMk2q2kbvf5NTOTW2HyQAu_0X8km1fWz1ie-WJrcnrCNRPN8yw !DOCTYPE html>html langen>head> meta charsetUTF-8> meta nameviewport contentwidthdevice-width, initial-scale1.0> title>DeeGee - Home/title> !-- Maintaining your Google Fonts import for Audiowide --> link relpreconnect hrefhttps://fonts.googleapis.com> link relpreconnect hrefhttps://fonts.gstatic.com crossorigin> link hrefhttps://fonts.googleapis.com/css2?familyAudiowide&displayswap relstylesheet> !-- Added favicon code from your original --> link relicon hrefdata:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+CiAgPGRlZnM+CiAgICA8ZmlsdGVyIGlkPSJnbG93IiB4PSItMzAlIiB5PSItMzAlIiB3aWR0aD0iMTYwJSIgaGVpZ2h0PSIxNjAlIj4KICAgICAgPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMiIgcmVzdWx0PSJibHVyIi8+CiAgICAgIDxmZUNvbXBvc2l0ZSBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJibHVyIiBvcGVyYXRvcj0ib3ZlciIvPgogICAgPC9maWx0ZXI+CiAgPC9kZWZzPgogIDxjaXJjbGUgY3g9IjE2IiBjeT0iMTYiIHI9IjEyIiBmaWxsPSJub25lIiBzdHJva2U9IiNGRjg4MDAiIHN0cm9rZS13aWR0aD0iMiIgZmlsdGVyPSJ1cmwoI2dsb3cpIi8+CiAgPHBhdGggZD0iTTgsNiBMMTQsMTYgTDE2LDEyIEwyMCwxOCBMMjQsOCIgc3Ryb2tlPSIjRkZENzAwIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiIGZpbHRlcj0idXJsKCNnbG93KSIvPgogIDxjaXJjbGUgY3g9IjE0IiBjeT0iMTYiIHI9IjEuNSIgZmlsbD0iI0ZGRDcwMCIvPgogIDxjaXJjbGUgY3g9IjE2IiBjeT0iMTIiIHI9IjEuNSIgZmlsbD0iI0ZGRDcwMCIvPgogIDxjaXJjbGUgY3g9IjIwIiBjeT0iMTgiIHI9IjEuNSIgZmlsbD0iI0ZGRDcwMCIvPgo8L3N2Zz4 typeimage/svg+xml> style> body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background-color: #000; font-family: Arial, sans-serif; } /* Keep your existing CSS styles */ #blackHoleContainer { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; cursor: grab; } #blackHoleContainer:active { cursor: grabbing; } /* Header with blurred backdrop */ .header { position: fixed; top: 0; left: 0; width: 100%; height: 80px; z-index: 100; display: flex; align-items: center; opacity: 0; transition: opacity 6s ease-in; padding: 0 20px; } .header.visible { opacity: 1; } .header-backdrop { position: absolute; top: 0; left: 0; width: 100%; height: 100%; backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); background-color: rgba(0, 0, 0, 0.2); z-index: -1; } /* Logo container */ .logo-container { width: 200px; height: 60px; margin-right: 20px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; cursor: pointer; } .logo { width: 100%; height: 100%; object-fit: contain; } /* Background Logo Container */ #backgroundLogoContainer { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 13vw; /* Increased by 30% from 10vw */ height: 13vh; /* Increased by 30% from 10vh */ display: flex; align-items: center; justify-content: center; z-index: 2; /* Increased z-index to ensure visibility */ opacity: 0; transition: opacity 2s ease-in-out; transform-style: preserve-3d; transform: translate(-50%, -50%) translateZ(-500px) scale(1); pointer-events: none; } #backgroundLogo { width: 100%; height: 100%; filter: none; -webkit-filter: drop-shadow(0 0 20px rgba(255, 215, 0, 0.8)); filter: drop-shadow(0 0 20px rgba(255, 215, 0, 0.8)); /* Add drop shadow for extra glow */ } /* Navigation container */ .nav-container { flex: 1; display: flex; justify-content: center; height: 100%; align-items: center; } .nav-buttons { display: flex; gap: 40px; } .nav-button { background: transparent; border: none; color: #B8860B; font-size: 16px; font-weight: 600; padding: 8px 12px; cursor: pointer; transition: all 0.3s ease; position: relative; text-transform: uppercase; letter-spacing: 1px; white-space: nowrap; font-family: Audiowide, cursive; } .nav-button:hover { color: #FFD700; } .nav-button::after { content: ; position: absolute; bottom: 0; left: 50%; width: 0; height: 2px; background: #FFD700; transition: all 0.3s ease; transform: translateX(-50%); } .nav-button:hover::after { width: 100%; } /* Mobile menu button */ .mobile-menu-button { display: none; width: 36px; height: 36px; background: rgba(0, 0, 0, 0.6); border: 2px solid #FFD700; border-radius: 4px; color: #FFD700; font-size: 24px; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s ease; margin-left: auto; z-index: 101; font-family: Audiowide, cursive; } .mobile-menu-button:hover { background: rgba(50, 50, 0, 0.7); } .light-effect { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient( ellipse at center, rgba(255, 215, 0, 0.05) 0%, rgba(0, 0, 0, 0) 70% ); z-index: 0; opacity: 0; transition: opacity 5s ease-in; } .light-effect.visible { opacity: 1; } /* Controls container */ .controls { position: fixed; top: 100px; right: 20px; z-index: 10; display: flex; flex-direction: column; gap: 12px; opacity: 0; transition: opacity 5s ease-in; } .controls.visible { opacity: 1; } /* Control button styling */ .control-button { width: 36px; height: 36px; border-radius: 50%; background: rgba(0, 0, 0, 0.6); border: 2px solid #FFD700; display: flex; justify-content: center; align-items: center; cursor: pointer; color: #FFD700; font-size: 18px; box-shadow: 0 0 15px rgba(255, 215, 0, 0.5); transition: all 0.3s ease; font-family: Audiowide, cursive; } .control-button:hover { background: rgba(50, 50, 0, 0.7); box-shadow: 0 0 20px rgba(255, 215, 0, 0.8); } .control-button.active { background: rgba(80, 70, 0, 0.7); box-shadow: 0 0 20px rgba(255, 215, 0, 1); } /* Label styling */ .control-label { position: absolute; right: 50px; color: #FFD700; background: rgba(0, 0, 0, 0.6); padding: 5px 10px; border-radius: 15px; font-family: Audiowide, cursive; font-size: 12px; white-space: nowrap; opacity: 0; transition: opacity 0.3s; pointer-events: none; } .control-button:hover + .control-label { opacity: 1; } /* Messages */ #autoplayMessage { position: fixed; bottom: 30px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.7); color: #FFD700; padding: 10px 20px; border-radius: 20px; font-family: Audiowide, cursive; font-size: 14px; border: 1px solid #FFD700; z-index: 100; cursor: pointer; opacity: 0; transition: opacity 0.5s; pointer-events: none; } #autoplayMessage.show { opacity: 1; pointer-events: auto; } #steeringMessage { position: fixed; bottom: 70px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.7); color: #FFD700; padding: 10px 20px; border-radius: 20px; font-family: Audiowide, cursive; font-size: 14px; border: 1px solid #FFD700; z-index: 100; opacity: 0; transition: opacity 0.5s; } #steeringMessage.visible.fade { opacity: 0; pointer-events: none; } #steeringMessage.visible { opacity: 1; } /* Center button */ #centerButton { position: fixed; bottom: 20px; right: 20px; width: 42px; height: 42px; border-radius: 50%; background: rgba(0, 0, 0, 0.6); border: 2px solid #FFD700; display: flex; justify-content: center; align-items: center; cursor: pointer; color: #FFD700; font-size: 18px; box-shadow: 0 0 15px rgba(255, 215, 0, 0.5); transition: all 0.3s ease; z-index: 10; opacity: 0; transition: opacity 5s ease-in; font-family: Audiowide, cursive; } #centerButton.visible { opacity: 0.7; } #centerButton:hover { background: rgba(50, 50, 0, 0.7); box-shadow: 0 0 20px rgba(255, 215, 0, 0.8); opacity: 1; } /* Zoom controls */ #zoomControls { position: fixed; right: 20px; bottom: 75px; display: flex; flex-direction: column; gap: 10px; z-index: 10; opacity: 0; transition: opacity 5s ease-in; } #zoomControls.visible { opacity: 1; } .zoom-button { width: 36px; height: 36px; border-radius: 50%; background: rgba(0, 0, 0, 0.6); border: 2px solid #FFD700; display: flex; justify-content: center; align-items: center; cursor: pointer; color: #FFD700; font-size: 18px; box-shadow: 0 0 15px rgba(255, 215, 0, 0.5); transition: all 0.3s ease; font-family: Audiowide, cursive; } .zoom-button:hover { background: rgba(50, 50, 0, 0.7); box-shadow: 0 0 20px rgba(255, 215, 0, 0.8); } /* Fade overlay */ #fadeOverlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: #000; z-index: 1000; pointer-events: none; opacity: 1; transition: opacity 4s ease-out; } #fadeOverlay.fade-out { opacity: 0; } /* Mobile navigation adjustments */ @media (max-width: 768px) { .mobile-menu-button { display: flex; } .nav-container { position: fixed; top: 80px; right: -200px; width: 200px; height: auto; background: rgba(0, 0, 0, 0.9); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); border-left: 1px solid rgba(255, 215, 0, 0.3); border-bottom: 1px solid rgba(255, 215, 0, 0.3); padding: 20px 0; transition: right 0.3s ease; justify-content: flex-start; z-index: 99; } .nav-container.mobile-open { right: 0; } .nav-buttons { flex-direction: column; gap: 15px; width: 100%; } .nav-button { font-size: 14px; padding: 8px 15px; width: 100%; text-align: center; } .logo-container { width: 160px; height: 50px; } #backgroundLogoContainer { width: 26vw; /* Increased by 30% from 20vw */ height: 26vw; /* Increased by 30% from 20vw */ } } @media (max-width: 480px) { .logo-container { width: 130px; height: 40px; } .nav-container { width: 180px; right: -180px; } .nav-button { font-size: 12px; padding: 7px 10px; } } /style>/head>body> div idfadeOverlay>/div> !-- Black hole container (replaces canvas) --> div idblackHoleContainer>/div> !-- Background logo container --> div idbackgroundLogoContainer> svg idbackgroundLogo viewBox0 0 600 300 xmlnshttp://www.w3.org/2000/svg> !-- Circle with glow --> circle cx300 cy150 r120 fillnone stroke#FF8800 stroke-width4 filterurl(#bigGlow)/> !-- Line chart with glow --> path dM150,200 L220,150 L280,180 L340,120 L400,150 L450,100 stroke#FFD700 stroke-width4 fillnone filterurl(#bigChartGlow)/> !-- Chart points --> circle cx220 cy150 r6 fill#FFD700 filterurl(#bigPointGlow)/> circle cx280 cy180 r6 fill#FFD700 filterurl(#bigPointGlow)/> circle cx340 cy120 r6 fill#FFD700 filterurl(#bigPointGlow)/> circle cx400 cy150 r6 fill#FFD700 filterurl(#bigPointGlow)/> !-- Arrow at end of chart --> path dM450,100 L470,80 L440,90 Z fill#FFD700 filterurl(#bigPointGlow)/> !-- Text DeeGee-Analytics with Audiowide font --> text x300 y250 text-anchormiddle font-familyAudiowide, Arial, sans-serif font-size50 font-weightbold fill#FF8800 filterurl(#textGlow)>DeeGee-Analytics/text> !-- Particle wave effect at bottom --> path dM100,280 Q200,240 300,280 Q400,320 500,280 stroke#FF8800 stroke-width1 fillnone opacity0.5/> path dM100,290 Q200,250 300,290 Q400,330 500,290 stroke#FF8800 stroke-width1 fillnone opacity0.3/> !-- Filters for glow effects --> defs> filter idbigGlow x-50% y-50% width200% height200%> feGaussianBlur stdDeviation16 resultblur/> !-- Increased from 12 to 16 for more luminosity --> feComposite inSourceGraphic in2blur operatorover/> /filter> filter idbigChartGlow x-50% y-50% width200% height200%> feGaussianBlur stdDeviation10 resultblur/> !-- Increased from 6 to 10 for more luminosity --> feComposite inSourceGraphic in2blur operatorover/> /filter> filter idbigPointGlow x-50% y-50% width200% height200%> feGaussianBlur stdDeviation8 resultblur/> !-- Increased from 4 to 8 for more luminosity --> feComposite inSourceGraphic in2blur operatorover/> /filter> filter idtextGlow x-50% y-50% width200% height200%> feGaussianBlur stdDeviation10 resultblur/> !-- Increased from 5 to 10 for more luminosity --> feComposite inSourceGraphic in2blur operatorover/> /filter> /defs> /svg> /div> !-- Header with navigation and logo --> header classheader> div classheader-backdrop>/div> !-- Logo container with inline SVG --> div classlogo-container idhomeLogoLink> svg classlogo viewBox0 0 600 300 xmlnshttp://www.w3.org/2000/svg> !-- Circle with glow --> circle cx300 cy150 r120 fillnone stroke#FF8800 stroke-width4 filterurl(#glow)/> !-- Line chart with glow --> path dM150,200 L220,150 L280,180 L340,120 L400,150 L450,100 stroke#FFD700 stroke-width4 fillnone filterurl(#chartGlow)/> !-- Chart points --> circle cx220 cy150 r6 fill#FFD700 filterurl(#pointGlow)/> circle cx280 cy180 r6 fill#FFD700 filterurl(#pointGlow)/> circle cx340 cy120 r6 fill#FFD700 filterurl(#pointGlow)/> circle cx400 cy150 r6 fill#FFD700 filterurl(#pointGlow)/> !-- Arrow at end of chart --> path dM450,100 L470,80 L440,90 Z fill#FFD700 filterurl(#pointGlow)/> !-- Text DeeGee-Analytics with Audiowide font --> text x300 y250 text-anchormiddle font-familyAudiowide, Arial, sans-serif font-size50 font-weightbold fill#FF8800>DeeGee-Analytics/text> !-- Particle wave effect at bottom --> path dM100,280 Q200,240 300,280 Q400,320 500,280 stroke#FF8800 stroke-width1 fillnone opacity0.5/> path dM100,290 Q200,250 300,290 Q400,330 500,290 stroke#FF8800 stroke-width1 fillnone opacity0.3/> !-- Filters for glow effects --> defs> filter idglow x-50% y-50% width200% height200%> feGaussianBlur stdDeviation8 resultblur/> feComposite inSourceGraphic in2blur operatorover/> /filter> filter idchartGlow x-50% y-50% width200% height200%> feGaussianBlur stdDeviation3 resultblur/> feComposite inSourceGraphic in2blur operatorover/> /filter> filter idpointGlow x-50% y-50% width200% height200%> feGaussianBlur stdDeviation2 resultblur/> feComposite inSourceGraphic in2blur operatorover/> /filter> /defs> /svg> /div> !-- Mobile menu button (hamburger) --> div idmobileMenuButton classmobile-menu-button>☰/div> !-- Navigation container --> div idnavContainer classnav-container> nav classnav-buttons> button classnav-button>Services/button> button classnav-button>Have Some Fun/button> /nav> /div> /header> div classlight-effect>/div> !-- Audio element --> audio idbackgroundMusic loop> source src796512__ilariio__weightless-atmosphere.mp3 typeaudio/mp3> source srcweightless-atmosphere.mp3 typeaudio/mp3> source srcsound.mp3 typeaudio/mp3> Your browser does not support the audio element. /audio> !-- Controls --> div classcontrols> div idsoundControl classcontrol-button active>♪/div> div classcontrol-label>Toggle Sound/div> div idanimationControl classcontrol-button active>▶/div> div classcontrol-label>Toggle Animation/div> /div> !-- Zoom controls --> div idzoomControls classzoom-controls> div idzoomInButton classzoom-button>+/div> div idzoomOutButton classzoom-button>-/div> /div> !-- Messages --> div idautoplayMessage>Click anywhere to enable sound/div> div idsteeringMessage>Drag to steer your view of the black hole/div> !-- Center view button --> div idcenterButton>⊕/div> !-- Include Three.js library --> script srchttps://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js>/script> script> // Control elements const backgroundMusic document.getElementById(backgroundMusic); const soundControl document.getElementById(soundControl); const animationControl document.getElementById(animationControl); const autoplayMessage document.getElementById(autoplayMessage); const steeringMessage document.getElementById(steeringMessage); const centerButton document.getElementById(centerButton); const zoomInButton document.getElementById(zoomInButton); const zoomOutButton document.getElementById(zoomOutButton); const fadeOverlay document.getElementById(fadeOverlay); const lightEffect document.querySelector(.light-effect); const controls document.querySelector(.controls); const zoomControls document.getElementById(zoomControls); const header document.querySelector(.header); const navButtons document.querySelectorAll(.nav-button); const mobileMenuButton document.getElementById(mobileMenuButton); const navContainer document.getElementById(navContainer); const backgroundLogoContainer document.getElementById(backgroundLogoContainer); const backgroundLogo document.getElementById(backgroundLogo); const blackHoleContainer document.getElementById(blackHoleContainer); const homeLogoLink document.getElementById(homeLogoLink); // Background logo animation variables let logoAnimationStarted false; let backgroundLogoState hidden; let backgroundLogoTimer null; let backgroundLogoGlowIntensity 0; let logoGlowInterval null; // Function to control background logo animation cycle function startBackgroundLogoAnimation() { if (logoAnimationStarted) return; logoAnimationStarted true; // Start the animation cycle animateBackgroundLogo(); } // Function to handle background logo animation states function animateBackgroundLogo() { // Fade in (2 seconds) backgroundLogoState fadingIn; backgroundLogoContainer.style.transition opacity 2s ease-in-out; backgroundLogoContainer.style.opacity 0; // Make sure filters are initially subtle const filters backgroundLogo.querySelectorAll(filter feGaussianBlur); filters.forEach(filter > { const currentId filter.parentNode.id; if (currentId bigGlow) { filter.setAttribute(stdDeviation, 16); } else if (currentId bigChartGlow) { filter.setAttribute(stdDeviation, 10); } else if (currentId bigPointGlow) { filter.setAttribute(stdDeviation, 8); } else if (currentId textGlow) { filter.setAttribute(stdDeviation, 10); } }); // Start fade in setTimeout(() > { backgroundLogoContainer.style.opacity 1; }, 100); // After fade in, start glowing (3 seconds) setTimeout(() > { backgroundLogoState glowing; backgroundLogoContainer.style.opacity 1; // Make the logo fully clear and sharp at peak glow backgroundLogo.style.filter none; // Intensify filters for maximum clarity and luminosity filters.forEach(filter > { const currentId filter.parentNode.id; if (currentId bigGlow) { filter.setAttribute(stdDeviation, 20); // Increased for more luminosity } else if (currentId bigChartGlow) { filter.setAttribute(stdDeviation, 12); // Increased for more luminosity } else if (currentId bigPointGlow) { filter.setAttribute(stdDeviation, 10); // Increased for more luminosity } else if (currentId textGlow) { filter.setAttribute(stdDeviation, 12); // Increased for more luminosity } }); // Apply a slight pulsating glow effect during the glow phase logoGlowInterval setInterval(() > { if (backgroundLogoState ! glowing) { clearInterval(logoGlowInterval); return; } // Pulse the overall scale slightly to create subtle movement const currentScale 1 + (Math.sin(Date.now() / 300) * 0.05); // Slightly more pronounced backgroundLogoContainer.style.transform `translate(-50%, -50%) translateZ(-500px) scale(${currentScale})`; // Also pulse the glow intensity slightly const glowIntensity 1 + (Math.sin(Date.now() / 400) * 0.2); // Increased variation // Apply the pulsating glow intensity to all elements filters.forEach(filter > { const currentId filter.parentNode.id; if (currentId bigGlow) { const base 20; // Increased from 16 filter.setAttribute(stdDeviation, (base * glowIntensity).toString()); } else if (currentId bigChartGlow) { const base 12; // Increased from 8 filter.setAttribute(stdDeviation, (base * glowIntensity).toString()); } else if (currentId bigPointGlow) { const base 10; // Increased from 5 filter.setAttribute(stdDeviation, (base * glowIntensity).toString()); } else if (currentId textGlow) { const base 12; // Increased from 7 filter.setAttribute(stdDeviation, (base * glowIntensity).toString()); } }); }, 50); // After glowing, fade out (2 seconds) setTimeout(() > { backgroundLogoState fadingOut; clearInterval(logoGlowInterval); // Reset to normal state before fade out backgroundLogoContainer.style.transform translate(-50%, -50%) translateZ(-500px) scale(1); // Reset filters to initial state filters.forEach(filter > { const currentId filter.parentNode.id; if (currentId bigGlow) { filter.setAttribute(stdDeviation, 16); } else if (currentId bigChartGlow) { filter.setAttribute(stdDeviation, 10); } else if (currentId bigPointGlow) { filter.setAttribute(stdDeviation, 8); } else if (currentId textGlow) { filter.setAttribute(stdDeviation, 10); } }); backgroundLogoContainer.style.transition opacity 2s ease-in-out; backgroundLogoContainer.style.opacity 0; // Wait for fade out, then after 10 seconds, restart cycle setTimeout(() > { backgroundLogoState hidden; // Wait 10 seconds before repeating setTimeout(() > { animateBackgroundLogo(); }, 10000); }, 2000); }, 3000); }, 2000); } // Mobile menu toggle mobileMenuButton.addEventListener(click, function() { navContainer.classList.toggle(mobile-open); mobileMenuButton.textContent navContainer.classList.contains(mobile-open) ? ✕ : ☰; }); // Close mobile menu when a nav button is clicked navButtons.forEach(button > { button.addEventListener(click, function() { if (window.innerWidth 768) { navContainer.classList.remove(mobile-open); mobileMenuButton.textContent ☰; } }); }); // Close mobile menu when clicking elsewhere document.addEventListener(click, function(e) { if (window.innerWidth 768 && navContainer.classList.contains(mobile-open) && !navContainer.contains(e.target) && e.target ! mobileMenuButton) { navContainer.classList.remove(mobile-open); mobileMenuButton.textContent ☰; } }); // Logo home navigation - REMOVED as requested homeLogoLink.addEventListener(click, function() { // Navigation to Home.html removed as requested }); // State variables let soundActive true; let animationActive true; let fadeInComplete false; // Camera variables let defaultCameraZ 8.5; // MODIFIED: Increased from 4.5 to 8.5 (8 clicks of zoom out from 4.5, where each click is +0.5) let currentCameraZ defaultCameraZ; let minCameraZ 2; let maxCameraZ 10; // View steering variables let isDragging false; let lastMouseX 0; let lastMouseY 0; let viewOffsetX 0; let viewOffsetY 0; let targetViewOffsetX 0; let targetViewOffsetY 0; const maxViewOffset 300; const viewSmoothingFactor 0.05; const dragSensitivity 1.5; // Ship control variables let steeringMomentum {x: 0, y: 0}; const momentumDecay 0.95; // THREE.js variables let scene, camera, renderer; let blackHoleGroup; let blackHoleTexture, blackHoleMaterial; let accretionDiskMesh; let jetParticleSystemTop, jetParticleSystemBottom; let particles ; let stars ; let time 0; let rotationSpeed 0.2; // Fade in function function fadeIn() { // Start fading out the black overlay setTimeout(() > { fadeOverlay.classList.add(fade-out); }, 1000); // Gradually bring in other elements setTimeout(() > { lightEffect.classList.add(visible); controls.classList.add(visible); centerButton.classList.add(visible); zoomControls.classList.add(visible); header.classList.add(visible); // Start the background logo animation cycle once the initial fade-in is complete setTimeout(() > { startBackgroundLogoAnimation(); }, 2000); // Start showing messages after elements appear setTimeout(() > { steeringMessage.classList.add(visible); // Hide steering message after 5 seconds setTimeout(() > { steeringMessage.classList.add(fade); fadeInComplete true; }, 5000); }, 1000); }, 2000); } // Attempt to play audio on page load function attemptAutoplay() { // Set volume before attempting to play backgroundMusic.volume 0.5; // Attempt to play const playPromise backgroundMusic.play(); // Handle the play promise if (playPromise ! undefined) { playPromise.then(() > { // Autoplay successful! console.log(Autoplay successful); soundActive true; soundControl.classList.add(active); }).catch(error > { // Autoplay was prevented console.log(Autoplay prevented:, error); soundActive false; soundControl.classList.remove(active); // Show help message autoplayMessage.classList.add(show); // Setup document-wide click to start audio document.addEventListener(click, function startAudioOnClick() { backgroundMusic.play() .then(() > { console.log(Audio started after click); soundActive true; soundControl.classList.add(active); autoplayMessage.classList.remove(show); }) .catch(e > console.error(Audio failed:, e)); document.removeEventListener(click, startAudioOnClick); }, { once: true }); // Auto-hide message after 5 seconds setTimeout(() > { autoplayMessage.classList.remove(show); }, 5000); }); } } // Sound control button soundControl.addEventListener(click, function(e) { e.stopPropagation(); soundActive !soundActive; if (soundActive) { backgroundMusic.play() .then(() > { soundControl.classList.add(active); }) .catch(e > { console.error(Audio failed:, e); soundActive false; soundControl.classList.remove(active); }); } else { backgroundMusic.pause(); soundControl.classList.remove(active); } }); // Animation control animationControl.addEventListener(click, function(e) { e.stopPropagation(); animationActive !animationActive; if (animationActive) { animationControl.textContent ▶; animationControl.classList.add(active); rotationSpeed 0.2; } else { animationControl.textContent ❚❚; animationControl.classList.remove(active); rotationSpeed 0; } }); // Zoom controls zoomInButton.addEventListener(click, function(e) { e.stopPropagation(); currentCameraZ Math.max(minCameraZ, currentCameraZ - 0.5); if (camera) { camera.position.z currentCameraZ; } }); zoomOutButton.addEventListener(click, function(e) { e.stopPropagation(); currentCameraZ Math.min(maxCameraZ, currentCameraZ + 0.5); if (camera) { camera.position.z currentCameraZ; } }); // Function to animate rotation to a target function animateRotationTo(targetRotation) { const startRotation { x: blackHoleGroup.rotation.x, y: blackHoleGroup.rotation.y, z: blackHoleGroup.rotation.z }; const duration 1000; // ms const startTime Date.now(); function updateRotation() { const elapsed Date.now() - startTime; const progress Math.min(elapsed / duration, 1); // Ease function (ease-in-out) const easeProgress progress 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2; blackHoleGroup.rotation.x startRotation.x + (targetRotation.x - startRotation.x) * easeProgress; blackHoleGroup.rotation.y startRotation.y + (targetRotation.y - startRotation.y) * easeProgress; blackHoleGroup.rotation.z startRotation.z + (targetRotation.z - startRotation.z) * easeProgress; if (progress 1) { requestAnimationFrame(updateRotation); } else { // Reset view offsets after transition completes targetViewOffsetX 0; targetViewOffsetY 0; viewOffsetX 0; viewOffsetY 0; steeringMomentum {x: 0, y: 0}; } } updateRotation(); } // Center view button centerButton.addEventListener(click, function(e) { e.stopPropagation(); // Smoothly return to center view const returnToCenter function() { targetViewOffsetX * 0.8; targetViewOffsetY * 0.8; steeringMomentum.x * 0.8; steeringMomentum.y * 0.8; if (Math.abs(targetViewOffsetX) > 0.5 || Math.abs(targetViewOffsetY) > 0.5) { requestAnimationFrame(returnToCenter); } else { targetViewOffsetX 0; targetViewOffsetY 0; steeringMomentum.x 0; steeringMomentum.y 0; } }; requestAnimationFrame(returnToCenter); }); // Autoplay help message click autoplayMessage.addEventListener(click, function() { backgroundMusic.play() .then(() > { soundActive true; soundControl.classList.add(active); autoplayMessage.classList.remove(show); }) .catch(e > console.error(Audio failed:, e)); }); // Navigate with fade effect function navigateWithFade(destination) { // Apply fadeout effect fadeOverlay.style.transition opacity 2s ease-in; fadeOverlay.classList.remove(fade-out); // Fade out music if its playing if (soundActive) { const fadeAudio setInterval(() > { if (backgroundMusic.volume > 0.05) { backgroundMusic.volume - 0.05; } else { backgroundMusic.pause(); clearInterval(fadeAudio); } }, 100); } // After fade completes, navigate to the new page setTimeout(() > { window.location.href destination; }, 2000); } // Initialize Three.js scene function initThreeScene() { // Create scene scene new THREE.Scene(); // Create camera camera new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 2000); camera.position.z defaultCameraZ; // Initial camera position zoomed out by 10% // Create renderer renderer new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); blackHoleContainer.appendChild(renderer.domElement); // Create black hole group blackHoleGroup new THREE.Group(); scene.add(blackHoleGroup); // Create stars createStars(); // Create procedural black hole createBlackHole(); // Handle window resize window.addEventListener(resize, onWindowResize); } // Create stars in the background function createStars() { // Increase star count by 15% (from 2600 to 2990) const starCount 2990; const starGeometry new THREE.BufferGeometry(); const starPositions new Float32Array(starCount * 3); const starSizes new Float32Array(starCount); const starColors new Float32Array(starCount * 3); for (let i 0; i starCount; i++) { // Position stars in a sphere around the scene const radius 100 + Math.random() * 900; const theta Math.random() * Math.PI * 2; const phi Math.acos(2 * Math.random() - 1); starPositionsi * 3 radius * Math.sin(phi) * Math.cos(theta); starPositionsi * 3 + 1 radius * Math.sin(phi) * Math.sin(theta); starPositionsi * 3 + 2 radius * Math.cos(phi); // Star sizes starSizesi Math.random() * 2 + 0.5; // Add more white stars (increasing from 30% to 45% white stars) if (Math.random() > 0.55) { // Yellowish stars starColorsi * 3 1.0; // R starColorsi * 3 + 1 0.9 + Math.random() * 0.1; // G starColorsi * 3 + 2 0.7 + Math.random() * 0.2; // B } else { // White/blue stars (more of these now) starColorsi * 3 0.9 + Math.random() * 0.1; // R - Increased to make more white starColorsi * 3 + 1 0.9 + Math.random() * 0.1; // G - Increased to make more white starColorsi * 3 + 2 0.9 + Math.random() * 0.1; // B - Increased to make more white } } starGeometry.setAttribute(position, new THREE.BufferAttribute(starPositions, 3)); starGeometry.setAttribute(size, new THREE.BufferAttribute(starSizes, 1)); starGeometry.setAttribute(color, new THREE.BufferAttribute(starColors, 3)); // Star shader material for better-looking stars const starMaterial new THREE.ShaderMaterial({ uniforms: { time: { value: 0 } }, vertexShader: ` attribute float size; attribute vec3 color; varying vec3 vColor; void main() { vColor color; vec4 mvPosition modelViewMatrix * vec4(position, 1.0); gl_PointSize size * (300.0 / -mvPosition.z); gl_Position projectionMatrix * mvPosition; } `, fragmentShader: ` varying vec3 vColor; void main() { float r length(gl_PointCoord - vec2(0.5, 0.5)); if (r > 0.5) discard; gl_FragColor vec4(vColor, 1.0) * (1.0 - r * 1.5); } `, transparent: true, depthWrite: false }); const starPoints new THREE.Points(starGeometry, starMaterial); scene.add(starPoints); stars.push(starPoints); } // Create the black hole and accretion disk function createBlackHole() { // Event horizon (black sphere) const blackHoleGeometry new THREE.SphereGeometry(1, 64, 64); const blackHoleMaterial new THREE.MeshBasicMaterial({ color: 0x000000, transparent: true, opacity: 0.9 }); const blackHoleMesh new THREE.Mesh(blackHoleGeometry, blackHoleMaterial); blackHoleGroup.add(blackHoleMesh); // Event horizon glow const glowGeometry new THREE.SphereGeometry(1.05, 64, 64); const glowMaterial new THREE.ShaderMaterial({ uniforms: { time: { value: 0 } }, vertexShader: ` varying vec3 vNormal; varying vec3 vPosition; void main() { vNormal normalize(normalMatrix * normal); vPosition position; gl_Position projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform float time; varying vec3 vNormal; varying vec3 vPosition; void main() { float intensity pow(0.5 - dot(vNormal, vec3(0, 0, 1.0)), 2.0); vec3 orange vec3(1.0, 0.4, 0.0); vec3 yellow vec3(1.0, 0.7, 0.0); vec3 color mix(orange, yellow, 0.5 + 0.5 * sin(time * 0.5)); gl_FragColor vec4(color, intensity * 0.8); } `, transparent: true, side: THREE.BackSide, depthWrite: false }); const glowMesh new THREE.Mesh(glowGeometry, glowMaterial); blackHoleGroup.add(glowMesh); // Accretion disk const diskGeometry new THREE.RingGeometry(1.2, 5, 128, 6); // Create a procedural texture for the accretion disk const diskTexture createAccretionDiskTexture(); // Shader material for accretion disk with rotation and distortion const diskMaterial new THREE.ShaderMaterial({ uniforms: { time: { value: 0 }, diskTexture: { value: diskTexture }, rotationSpeed: { value: 0.2 } }, vertexShader: ` varying vec2 vUv; varying vec3 vPosition; void main() { vUv uv; vPosition position; gl_Position projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform float time; uniform float rotationSpeed; uniform sampler2D diskTexture; varying vec2 vUv; varying vec3 vPosition; void main() { vec2 centeredUv vUv * 2.0 - 1.0; float angle atan(centeredUv.y, centeredUv.x); float dist length(centeredUv); // Rotation based on distance (faster when closer to center) float rotationFactor 1.0 / (0.5 + dist); float rotation time * rotationSpeed * rotationFactor; // Distort UVs based on rotation vec2 distortedUv vec2( 0.5 + cos(angle + rotation) * dist * 0.5, 0.5 + sin(angle + rotation) * dist * 0.5 ); // Sample the texture with our distorted UVs vec4 color texture2D(diskTexture, distortedUv); // Make inner part of disk brighter float brightnessFactor mix(3.0, 1.0, smoothstep(0.0, 1.0, dist)); color.rgb * brightnessFactor; // Add some temporal variation color.rgb + vec3(0.1, 0.05, 0.0) * sin(time * 2.0 + dist * 10.0); // Make edges fade out float edge smoothstep(0.0, 0.1, 1.0 - dist) * smoothstep(0.0, 0.1, dist); color.a * edge; gl_FragColor color; } `, transparent: true, side: THREE.DoubleSide, depthWrite: false }); accretionDiskMesh new THREE.Mesh(diskGeometry, diskMaterial); accretionDiskMesh.rotation.x Math.PI / 2.5; // Tilt the disk slightly blackHoleGroup.add(accretionDiskMesh); // Create particle-based jets from black hole poles createJetParticles(); // Add gravitational lensing effect createLensingEffect(); } // Create procedural texture for accretion disk function createAccretionDiskTexture() { const canvas document.createElement(canvas); canvas.width 1024; canvas.height 1024; const ctx canvas.getContext(2d); // Fill with black ctx.fillStyle black; ctx.fillRect(0, 0, canvas.width, canvas.height); // Create radial gradient for disk const centerX canvas.width / 2; const centerY canvas.height / 2; const gradient ctx.createRadialGradient( centerX, centerY, canvas.width * 0.1, centerX, centerY, canvas.width * 0.5 ); // Hot inner disk (yellowish-white) gradient.addColorStop(0, rgba(255, 255, 220, 1.0)); // Middle disk (orange to red) gradient.addColorStop(0.2, rgba(255, 175, 50, 0.95)); gradient.addColorStop(0.5, rgba(255, 80, 10, 0.9)); // Outer disk (dark red to black) gradient.addColorStop(0.8, rgba(180, 20, 0, 0.8)); gradient.addColorStop(1.0, rgba(50, 0, 0, 0)); ctx.fillStyle gradient; ctx.fillRect(0, 0, canvas.width, canvas.height); // Add streaks for detail ctx.save(); ctx.translate(centerX, centerY); for (let a 0; a Math.PI * 2; a + Math.PI / 12) { const streakLength canvas.width * (0.3 + Math.random() * 0.2); const streakWidth 5 + Math.random() * 20; ctx.save(); ctx.rotate(a); // Create gradient for streak const streakGradient ctx.createLinearGradient( canvas.width * 0.15, 0, streakLength, 0 ); streakGradient.addColorStop(0, rgba(255, 200, 50, 0.8)); streakGradient.addColorStop(0.5, rgba(255, 100, 0, 0.6)); streakGradient.addColorStop(1, rgba(100, 0, 0, 0)); ctx.fillStyle streakGradient; ctx.fillRect(canvas.width * 0.15, -streakWidth/2, streakLength, streakWidth); ctx.restore(); } ctx.restore(); // Convert to texture const texture new THREE.CanvasTexture(canvas); texture.wrapS THREE.RepeatWrapping; texture.wrapT THREE.RepeatWrapping; return texture; } // Create particle-based jets for black hole function createJetParticles() { // Define particle count for each jet const particleCount 2000; // Top Jet Particles const topJetParticles new THREE.BufferGeometry(); const topPositions new Float32Array(particleCount * 3); const topColors new Float32Array(particleCount * 3); const topSizes new Float32Array(particleCount); const topVelocities new Float32Array(particleCount * 3); const topOpacities new Float32Array(particleCount); const topAges new Float32Array(particleCount); const topMaxAges new Float32Array(particleCount); // Bottom Jet Particles const bottomJetParticles new THREE.BufferGeometry(); const bottomPositions new Float32Array(particleCount * 3); const bottomColors new Float32Array(particleCount * 3); const bottomSizes new Float32Array(particleCount); const bottomVelocities new Float32Array(particleCount * 3); const bottomOpacities new Float32Array(particleCount); const bottomAges new Float32Array(particleCount); const bottomMaxAges new Float32Array(particleCount); // Array of possible particle colors for the jets const particleColors {r: 1.0, g: 1.0, b: 0.7}, // Bright yellow {r: 1.0, g: 0.9, b: 0.4}, // Yellow {r: 1.0, g: 0.7, b: 0.2}, // Orange-yellow {r: 1.0, g: 0.5, b: 0.1}, // Orange {r: 1.0, g: 0.3, b: 0.0}, // Red-orange {r: 0.8, g: 0.2, b: 0.0}, // Red {r: 0.5, g: 0.0, b: 0.0}, // Dark red {r: 0.2, g: 0.05, b: 0.0} // Very dark red/black ; // Initialize particles for top jet for (let i 0; i particleCount; i++) { // Random starting position around the black holes north pole (thinner jet) const angle Math.random() * Math.PI * 2; const radius Math.random() * 0.05; // Reduced for thinner jet topPositionsi * 3 Math.cos(angle) * radius; topPositionsi * 3 + 1 1.0 + Math.random() * 0.1; // Slightly above the north pole topPositionsi * 3 + 2 Math.sin(angle) * radius; // Random velocity (mainly upward for top jet) // REDUCED by 15% from current speed (which was already 80% of original) // Current is 0.8 of original, so new is 0.8 * 0.85 0.68 of original const speed (0.005 + Math.random() * 0.01) * 0.4; const spread 0.1 + Math.random() * 0.1; // Reduced spread for thinner jet topVelocitiesi * 3 (Math.random() - 0.5) * spread; topVelocitiesi * 3 + 1 speed; // Upward topVelocitiesi * 3 + 2 (Math.random() - 0.5) * spread; // Random size (smaller for thinner jet) topSizesi 0.5 + Math.random() * 2; // Reduced from 1-4 to 0.5-2.5 // Random color from the color palette const color particleColorsMath.floor(Math.random() * particleColors.length); topColorsi * 3 color.r; topColorsi * 3 + 1 color.g; topColorsi * 3 + 2 color.b; // Initialize age and max age (longer lifespans for particles to reach further) topAgesi Math.random() * 250; // Start particles at different life stages topMaxAgesi 250 + Math.random() * 250; // Increased lifespan to reach top of screen // Initialize opacity topOpacitiesi 0.2 + Math.random() * 0.8; } // Initialize particles for bottom jet for (let i 0; i particleCount; i++) { // Random starting position around the black holes south pole (thinner jet) const angle Math.random() * Math.PI * 2; const radius Math.random() * 0.05; // Reduced for thinner jet bottomPositionsi * 3 Math.cos(angle) * radius; bottomPositionsi * 3 + 1 -1.0 - Math.random() * 0.1; // Slightly below the south pole bottomPositionsi * 3 + 2 Math.sin(angle) * radius; // Random velocity (mainly downward for bottom jet) // REDUCED by 15% from current speed (which was already 80% of original) // Current is 0.8 of original, so new is 0.8 * 0.85 0.68 of original const speed (0.005 + Math.random() * 0.01) * 0.68; const spread 0.1 + Math.random() * 0.1; // Reduced spread for thinner jet bottomVelocitiesi * 3 (Math.random() - 0.5) * spread; bottomVelocitiesi * 3 + 1 -speed; // Downward bottomVelocitiesi * 3 + 2 (Math.random() - 0.5) * spread; // Random size (smaller for thinner jet) bottomSizesi 0.5 + Math.random() * 2; // Reduced from 1-4 to 0.5-2.5 // Random color from the color palette const color particleColorsMath.floor(Math.random() * particleColors.length); bottomColorsi * 3 color.r; bottomColorsi * 3 + 1 color.g; bottomColorsi * 3 + 2 color.b; // Initialize age and max age (longer lifespans for particles to reach further) bottomAgesi Math.random() * 150; // Start particles at different life stages bottomMaxAgesi 150 + Math.random() * 150; // Increased lifespan to reach bottom of screen // Initialize opacity bottomOpacitiesi 0.2 + Math.random() * 0.8; } // Set attributes for top jet geometry topJetParticles.setAttribute(position, new THREE.BufferAttribute(topPositions, 3)); topJetParticles.setAttribute(color, new THREE.BufferAttribute(topColors, 3)); topJetParticles.setAttribute(size, new THREE.BufferAttribute(topSizes, 1)); topJetParticles.setAttribute(opacity, new THREE.BufferAttribute(topOpacities, 1)); // Create custom material for jet particles with glow effect const jetMaterial new THREE.ShaderMaterial({ uniforms: { time: { value: 0.0 } }, vertexShader: ` attribute float size; attribute vec3 color; attribute float opacity; varying vec3 vColor; varying float vOpacity; void main() { vColor color; vOpacity opacity; vec4 mvPosition modelViewMatrix * vec4(position, 1.0); gl_PointSize size * (300.0 / -mvPosition.z); gl_Position projectionMatrix * mvPosition; } `, fragmentShader: ` varying vec3 vColor; varying float vOpacity; void main() { // Create a soft circular particle with fade at edges float r length(gl_PointCoord - vec2(0.5, 0.5)); if (r > 0.5) discard; // Soft particle edge with higher center intensity float intensity 1.0 - (r * 2.0); intensity pow(intensity, 1.5); // Make center brighter // Final color with opacity gl_FragColor vec4(vColor, vOpacity * intensity); } `, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending }); // Set attributes for bottom jet geometry bottomJetParticles.setAttribute(position, new THREE.BufferAttribute(bottomPositions, 3)); bottomJetParticles.setAttribute(color, new THREE.BufferAttribute(bottomColors, 3)); bottomJetParticles.setAttribute(size, new THREE.BufferAttribute(bottomSizes, 1)); bottomJetParticles.setAttribute(opacity, new THREE.BufferAttribute(bottomOpacities, 1)); // Create particle systems jetParticleSystemTop new THREE.Points(topJetParticles, jetMaterial.clone()); jetParticleSystemBottom new THREE.Points(bottomJetParticles, jetMaterial.clone()); // Store particle data for animation jetParticleSystemTop.userData { positions: topPositions, velocities: topVelocities, colors: topColors, sizes: topSizes, opacities: topOpacities, ages: topAges, maxAges: topMaxAges }; jetParticleSystemBottom.userData { positions: bottomPositions, velocities: bottomVelocities, colors: bottomColors, sizes: bottomSizes, opacities: bottomOpacities, ages: bottomAges, maxAges: bottomMaxAges }; // Add to black hole group blackHoleGroup.add(jetParticleSystemTop); blackHoleGroup.add(jetParticleSystemBottom); } // Add gravitational lensing effect function createLensingEffect() { // Create distortion sphere const lensingGeometry new THREE.SphereGeometry(6, 64, 64); // Shader material for gravitational lensing const lensingMaterial new THREE.ShaderMaterial({ uniforms: { time: { value: 0 } }, vertexShader: ` varying vec3 vNormal; varying vec3 vPosition; void main() { vNormal normalize(normalMatrix * normal); vPosition position; gl_Position projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform float time; varying vec3 vNormal; varying vec3 vPosition; void main() { // Calculate distortion factor based on viewing angle float distortion pow(abs(dot(normalize(vPosition), vec3(0.0, 0.0, 1.0))), 10.0); // Add some time variation distortion * (0.9 + 0.1 * sin(time)); gl_FragColor vec4(0.0, 0.0, 0.0, distortion * 0.05); } `, transparent: true, side: THREE.DoubleSide, depthWrite: false }); const lensingMesh new THREE.Mesh(lensingGeometry, lensingMaterial); blackHoleGroup.add(lensingMesh); } // Handle window resize function onWindowResize() { camera.aspect window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } // Update steering function updateSteering() { // Apply steering momentum to the view if (!isDragging) { // Apply momentum decay when not actively dragging steeringMomentum.x * momentumDecay; steeringMomentum.y * momentumDecay; // Apply the momentum to the target view offset targetViewOffsetX + steeringMomentum.x; targetViewOffsetY + steeringMomentum.y; // Clamp to maximum values targetViewOffsetX Math.max(-maxViewOffset, Math.min(maxViewOffset, targetViewOffsetX)); targetViewOffsetY Math.max(-maxViewOffset, Math.min(maxViewOffset, targetViewOffsetY)); // If momentum is very small, stop it completely to avoid tiny drifts if (Math.abs(steeringMomentum.x) 0.01) steeringMomentum.x 0; if (Math.abs(steeringMomentum.y) 0.01) steeringMomentum.y 0; } // Smoothly transition view offset viewOffsetX + (targetViewOffsetX - viewOffsetX) * viewSmoothingFactor; viewOffsetY + (targetViewOffsetY - viewOffsetY) * viewSmoothingFactor; // Apply the view offset to the black holes rotation if (blackHoleGroup) { // Convert offset to rotation (inverted for natural feel) blackHoleGroup.rotation.y -viewOffsetX * 0.001; // Allow greater vertical rotation range in both directions blackHoleGroup.rotation.x -viewOffsetY * 0.001; // Limit x rotation to prevent flipping upside down blackHoleGroup.rotation.x Math.max(-Math.PI/2 + 0.1, Math.min(Math.PI/2 - 0.1, blackHoleGroup.rotation.x)); } } // Update particle-based jets function updateJetParticles() { if (!jetParticleSystemTop || !jetParticleSystemBottom || !animationActive) return; // Get window dimensions for calculating jet travel distances const windowHeight window.innerHeight; const distanceToTop 50; // Distance in world units to reach top of screen const distanceToBottom 50; // Distance in world units to reach bottom of screen // Update top jet particles const topData jetParticleSystemTop.userData; const topPositions topData.positions; const topVelocities topData.velocities; const topOpacities topData.opacities; const topAges topData.ages; const topMaxAges topData.maxAges; for (let i 0; i topPositions.length / 3; i++) { // Update age of particle topAgesi + 1; // If particle has reached its maximum age or traveled too far, reset it if (topAgesi > topMaxAgesi || topPositionsi * 3 + 1 > distanceToTop) { // Reset particle position to base of jet const angle Math.random() * Math.PI * 2; const radius Math.random() * 0.05; // Thinner jet topPositionsi * 3 Math.cos(angle) * radius; topPositionsi * 3 + 1 1.0 + Math.random() * 0.1; topPositionsi * 3 + 2 Math.sin(angle) * radius; // Reset velocity (reduced to 68% of original speed - further reduced by 15%) const speed (0.005 + Math.random() * 0.01) * 0.68; const spread 0.1 + Math.random() * 0.1; topVelocitiesi * 3 (Math.random() - 0.5) * spread; topVelocitiesi * 3 + 1 speed; topVelocitiesi * 3 + 2 (Math.random() - 0.5) * spread; // Reset age topAgesi 0; topMaxAgesi 250 + Math.random() * 250; // Reset opacity topOpacitiesi 0.2 + Math.random() * 0.8; } else { // Move particle based on velocity topPositionsi * 3 + topVelocitiesi * 3; topPositionsi * 3 + 1 + topVelocitiesi * 3 + 1; topPositionsi * 3 + 2 + topVelocitiesi * 3 + 2; // Apply some turbulence (reduced for smoother flow) const turbulenceFactor 0.0005; // Reduced from 0.002 topPositionsi * 3 + (Math.random() - 0.5) * turbulenceFactor; topPositionsi * 3 + 2 + (Math.random() - 0.5) * turbulenceFactor; // Gradually straighten particles as they move further from the black hole // This creates a more focused jet stream toward the top of the screen const distanceFromOrigin topPositionsi * 3 + 1 - 1.0; if (distanceFromOrigin > 2.0) { // Reduce horizontal components of velocity to create a straighter path topVelocitiesi * 3 * 0.99; topVelocitiesi * 3 + 2 * 0.99; // Ensure constant upward speed if (topVelocitiesi * 3 + 1 0.01) { topVelocitiesi * 3 + 1 + 0.0001; } } // Reduce opacity as particles age const lifeRatio topAgesi / topMaxAgesi; topOpacitiesi Math.max(0, topOpacitiesi * (1 - lifeRatio * 0.005)); } } // Update bottom jet particles const bottomData jetParticleSystemBottom.userData; const bottomPositions bottomData.positions; const bottomVelocities bottomData.velocities; const bottomOpacities bottomData.opacities; const bottomAges bottomData.ages; const bottomMaxAges bottomData.maxAges; for (let i 0; i bottomPositions.length / 3; i++) { // Update age of particle bottomAgesi + 1; // If particle has reached its maximum age or traveled too far, reset it if (bottomAgesi > bottomMaxAgesi || bottomPositionsi * 3 + 1 -distanceToBottom) { // Reset particle position to base of jet const angle Math.random() * Math.PI * 2; const radius Math.random() * 0.05; // Thinner jet bottomPositionsi * 3 Math.cos(angle) * radius; bottomPositionsi * 3 + 1 -1.0 - Math.random() * 0.1; bottomPositionsi * 3 + 2 Math.sin(angle) * radius; // Reset velocity (reduced to 68% of original speed - further reduced by 15%) const speed (0.005 + Math.random() * 0.01) * 0.68; const spread 0.1 + Math.random() * 0.1; bottomVelocitiesi * 3 (Math.random() - 0.5) * spread; bottomVelocitiesi * 3 + 1 -speed; bottomVelocitiesi * 3 + 2 (Math.random() - 0.5) * spread; // Reset age bottomAgesi 0; bottomMaxAgesi 150 + Math.random() * 150; // Reset opacity bottomOpacitiesi 0.2 + Math.random() * 0.8; } else { // Move particle based on velocity bottomPositionsi * 3 + bottomVelocitiesi * 3; bottomPositionsi * 3 + 1 + bottomVelocitiesi * 3 + 1; bottomPositionsi * 3 + 2 + bottomVelocitiesi * 3 + 2; // Apply some turbulence (reduced for smoother flow) const turbulenceFactor 0.0005; // Reduced from 0.002 bottomPositionsi * 3 + (Math.random() - 0.5) * turbulenceFactor; bottomPositionsi * 3 + 2 + (Math.random() - 0.5) * turbulenceFactor; // Gradually straighten particles as they move further from the black hole // This creates a more focused jet stream toward the bottom of the screen const distanceFromOrigin Math.abs(bottomPositionsi * 3 + 1 + 1.0); if (distanceFromOrigin > 2.0) { // Reduce horizontal components of velocity to create a straighter path bottomVelocitiesi * 3 * 0.99; bottomVelocitiesi * 3 + 2 * 0.99; // Ensure constant downward speed if (Math.abs(bottomVelocitiesi * 3 + 1) 0.01) { bottomVelocitiesi * 3 + 1 - 0.0001; } } // Reduce opacity as particles age const lifeRatio bottomAgesi / bottomMaxAgesi; bottomOpacitiesi Math.max(0, bottomOpacitiesi * (1 - lifeRatio * 0.01)); } } // Update the BufferAttributes with new data jetParticleSystemTop.geometry.attributes.position.needsUpdate true; jetParticleSystemTop.geometry.attributes.opacity.needsUpdate true; jetParticleSystemBottom.geometry.attributes.position.needsUpdate true; jetParticleSystemBottom.geometry.attributes.opacity.needsUpdate true; } // Animation loop function animate() { requestAnimationFrame(animate); // Update time time + 0.01; // Update steering updateSteering(); // Update particle-based jets updateJetParticles(); // Update shader uniforms if (blackHoleGroup) { blackHoleGroup.traverse(child > { if (child.isMesh && child.material.uniforms) { // Update time uniform if (child.material.uniforms.time ! undefined) { child.material.uniforms.time.value time; } // Update rotation speed based on animation state if (child.material.uniforms.rotationSpeed ! undefined) { child.material.uniforms.rotationSpeed.value rotationSpeed; } } else if (child.isPoints && child.material.uniforms) { // Update time uniform for particle systems if (child.material.uniforms.time ! undefined) { child.material.uniforms.time.value time; } } }); // Add gentle wobble to the black hole if (animationActive) { blackHoleGroup.rotation.z Math.sin(time * 0.2) * 0.05; } } // Render scene renderer.render(scene, camera); } // Mouse and touch event handlers for steering blackHoleContainer.addEventListener(mousedown, function(e) { if (e.target renderer.domElement) { isDragging true; lastMouseX e.clientX; lastMouseY e.clientY; blackHoleContainer.style.cursor grabbing; } }); document.addEventListener(mouseup, function() { isDragging false; blackHoleContainer.style.cursor grab; }); document.addEventListener(mousemove, function(e) { if (isDragging) { // Calculate how much the mouse has moved const dx e.clientX - lastMouseX; const dy e.clientY - lastMouseY; // Update target view offsets (invert direction for natural steering) targetViewOffsetX - dx * dragSensitivity; targetViewOffsetY - dy * dragSensitivity; // Update steering momentum based on mouse movement steeringMomentum.x -dx * 0.1; steeringMomentum.y -dy * 0.1; // Clamp to maximum values targetViewOffsetX Math.max(-maxViewOffset, Math.min(maxViewOffset, targetViewOffsetX)); targetViewOffsetY Math.max(-maxViewOffset, Math.min(maxViewOffset, targetViewOffsetY)); // Update last positions lastMouseX e.clientX; lastMouseY e.clientY; } }); // Touch support for mobile steering blackHoleContainer.addEventListener(touchstart, function(e) { e.preventDefault(); // Only start dragging if were touching the canvas (not header or other UI) const touch e.touches0; const target document.elementFromPoint(touch.clientX, touch.clientY); if (target renderer.domElement) { isDragging true; if (e.touches.length > 0) { lastMouseX e.touches0.clientX; lastMouseY e.touches0.clientY; } } }); document.addEventListener(touchend, function() { isDragging false; }); document.addEventListener(touchmove, function(e) { if (isDragging && e.touches.length > 0) { e.preventDefault(); const touch e.touches0; // Calculate how much the touch has moved const dx touch.clientX - lastMouseX; const dy touch.clientY - lastMouseY; // Update target view offsets (invert direction for natural steering) targetViewOffsetX - dx * dragSensitivity; targetViewOffsetY - dy * dragSensitivity; // Update steering momentum based on touch movement steeringMomentum.x -dx * 0.1; steeringMomentum.y -dy * 0.1; // Clamp to maximum values targetViewOffsetX Math.max(-maxViewOffset, Math.min(maxViewOffset, targetViewOffsetX)); targetViewOffsetY Math.max(-maxViewOffset, Math.min(maxViewOffset, targetViewOffsetY)); // Update last positions lastMouseX touch.clientX; lastMouseY touch.clientY; } }); // Initialize and start window.addEventListener(load, function() { // Initialize Three.js scene initThreeScene(); // Start animation animate(); // Start the fade-in sequence fadeIn(); // Try to autoplay audio setTimeout(attemptAutoplay, 1500); }); // Get all navigation buttons document.querySelectorAll(.nav-button).forEach(button > { button.addEventListener(click, function(e) { e.preventDefault(); // Prevent default behavior // Determine the destination URL based on button text let destination; const buttonText this.textContent.trim(); if (buttonText Contact Us) { destination ContactUs.html; } else if (buttonText Services) { destination Services.html; } else if (buttonText Have Some Fun) { destination HaveSomeFun.html; } if (destination) { navigateWithFade(destination); } }); }); /script>/body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]