Download engine.html from NeelAniGamer/Class_Of_Learners: direct link, hf CLI and curl.
- Browser
- Download file 49.3 kB
-
https://huggingface.co/spaces/NeelAniGamer/Class_Of_Learners/resolve/main/engine.html
- Command line
-
hf download hf://spaces/NeelAniGamer/Class_Of_Learners/engine.html
-
curl -L -o engine.html https://huggingface.co/spaces/NeelAniGamer/Class_Of_Learners/resolve/main/engine.html
49.3 kB
| <html lang="en"> | |
| <head> | |
| <meta name="description" content="Solar System Visualization Engine — 3D interactive solar system with realistic orbits, planet data, and space exploration." /> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Solar System Visualization Engine v3.3</title> | |
| <script type="importmap"> | |
| { | |
| "imports": { | |
| "three": "https://unpkg.com/three@0.160.0/build/three.module.js", | |
| "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/" | |
| } | |
| } | |
| </script> | |
| <link rel="preconnect" href="https://fonts.googleapis.com" /> | |
| <link rel="stylesheet" href="visual.css" /> | |
| <link rel="canonical" href="https://advancedlogiclabs.dpdns.org/engine" /> | |
| <script src="https://accounts.google.com/gsi/client" async defer></script> | |
| <script defer src="col-router.js"></script> | |
| <link rel="stylesheet" href="col-ui.css?v=1782446126.5715783" /> | |
| <script defer src="col-ui.js"></script> | |
| <script defer src="col-auth.js"></script> | |
| <meta name="google-site-verification" content="bWaer2b60VA1y3RMV48HYGPv8vlMUcvlFGxY3e6SAqU" /> | |
| <link rel="manifest" href="manifest.json" /> | |
| <meta name="apple-mobile-web-app-capable" content="yes" /> | |
| <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> | |
| <link rel="icon" type="image/png" href="Icon.png" /> | |
| </head> | |
| <body> | |
| <nav class="top-nav"> | |
| <a href="home" class="nav-brand"> | |
| <img src="Class.png" alt="Class Of Learners Logo" class="brand-logo" width="56" height="56" fetchpriority="high" decoding="async" /> | |
| <span class="brand-name">Class Of Learners</span> | |
| </a> | |
| <button class="mmb" id="mmb"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <line x1="3" y1="12" x2="21" y2="12"></line> | |
| <line x1="3" y1="6" x2="21" y2="6"></line> | |
| <line x1="3" y1="18" x2="21" y2="18"></line> | |
| </svg> | |
| </button> | |
| <div class="nav-links" id="navLinks"> | |
| <a href="home" class="nav-link">Home</a> | |
| <a href="about" class="nav-link">About</a> | |
| <a href="school" class="nav-link">School</a> | |
| <a href="sneh-asha" class="nav-link">Sneh Asha</a> | |
| <div class="dropdown" onclick="this.classList.toggle('active')"> | |
| <button class="dropdown-btn">Projects <span class="darr">▼</span></button> | |
| <div class="dropdown-content"> | |
| <div class="dropdown-content-inner"> | |
| <a href="ati">Typing Instructor <span>⌨️</span></a> | |
| <a href="solar">Solar Engine <span>🌍</span></a> | |
| <a href="gesture">Gesture Control <span>🖐️</span></a> | |
| <a href="rpg">RPG Game <span>⚔️</span></a> | |
| <a href="qr">QR Generator <span>⬛</span></a> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="tw"> | |
| <span class="tl" id="tLabel">Light</span> | |
| <label class="tsb"> | |
| <input type="checkbox" id="tsck" onchange="window.toggleTheme(this)" /> | |
| <div class="tst"><div class="tsth"></div></div> | |
| </label> | |
| </div> | |
| <button class="nav-login-btn" id="navLoginBtn" onclick="if (window.openGlobalLogin) window.openGlobalLogin()"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path> | |
| <circle cx="12" cy="7" r="4"></circle> | |
| </svg> | |
| Sign In | |
| </button> | |
| <div class="nav-user-profile" id="navUserProfile" onclick="if (window.openGlobalLogin) window.openGlobalLogin()"> | |
| <div class="nav-user-avatar" id="navUserAv">?</div> | |
| <span class="nun" id="navUserName">User</span> | |
| </div> | |
| <a href="download" class="nav-dl-btn mobile-dl">Download Hub</a> | |
| </div> | |
| <a href="download" class="nav-dl-btn desktop-dl">Download Hub</a> | |
| </nav> | |
| <div class="cd" id="cDot"></div> | |
| <div class="cr" id="cRing"></div> | |
| <script> | |
| ;(function () { | |
| try { | |
| if (localStorage.getItem('theme') === 'dark') { | |
| document.body.classList.add('dark-mode') | |
| } | |
| } catch (e) {} | |
| })() | |
| </script> | |
| <div id="measure-display" style="display: none"> | |
| <span id="measure-icon">�x�</span> | |
| <span id="measure-text">Select first object⬦</span> | |
| <button id="measure-close" onclick="window.toggleMeasure && window.toggleMeasure()">�S"</button> | |
| </div> | |
| <div id="homepage"> | |
| <canvas id="home-canvas"></canvas> | |
| <div id="home-solar-rings"> | |
| <div class="ring ring-1"></div> | |
| <div class="ring ring-2"></div> | |
| <div class="ring ring-3"></div> | |
| <div class="ring-planet rp-1"></div> | |
| <div class="ring-planet rp-2"></div> | |
| <div class="ring-planet rp-3"></div> | |
| <div id="home-sun-core"></div> | |
| </div> | |
| <div id="home-content"> | |
| <div id="home-badge"> | |
| <span class="badge-dot"></span> | |
| SOLAR ENGINE · v3.3 | |
| <span class="badge-dot"></span> | |
| </div> | |
| <h1 id="home-title"> | |
| <span class="ht-line">EXPLORE</span> | |
| <span class="ht-line ht-accent">THE UNIVERSE</span> | |
| </h1> | |
| <p id="home-tagline"> | |
| Journey through 100+ celestial objects � from Mercury's scorched plains<br /> | |
| to dark energy at the edge of the observable universe. | |
| </p> | |
| <div id="home-stats"> | |
| <div class="hs-item" data-count="8" data-label="PLANETS"></div> | |
| <div class="hs-divider"></div> | |
| <div class="hs-item" data-count="100" data-label="OBJECTS" data-suffix="+"></div> | |
| <div class="hs-divider"></div> | |
| <div class="hs-item" data-count="20" data-label="MOONS" data-suffix="+"></div> | |
| <div class="hs-divider"></div> | |
| <div class="hs-item" data-count="��~" data-label="DISCOVERY"></div> | |
| </div> | |
| <div id="home-features"> | |
| <div class="hf-card"> | |
| <div class="hf-icon">�x�</div> | |
| <div class="hf-text"> | |
| <div class="hf-title">REAL ORBITAL MECHANICS</div> | |
| <div class="hf-sub">Accurate period simulation at variable speed</div> | |
| </div> | |
| </div> | |
| <div class="hf-card"> | |
| <div class="hf-icon">��</div> | |
| <div class="hf-text"> | |
| <div class="hf-title">GALACTIC ZOOM</div> | |
| <div class="hf-sub">Pan from Earth orbit to the Milky Way</div> | |
| </div> | |
| </div> | |
| <div class="hf-card"> | |
| <div class="hf-icon">⬡</div> | |
| <div class="hf-text"> | |
| <div class="hf-title">DEEP DATABANK</div> | |
| <div class="hf-sub">Physics, atmosphere & mission data for every object</div> | |
| </div> | |
| </div> | |
| </div> | |
| <button id="launch-btn" onclick="enterApp()"> | |
| <span class="lb-icon">��</span> | |
| <span class="lb-text">ENTER THE SOLAR SYSTEM</span> | |
| <span class="lb-arrow">� </span> | |
| </button> | |
| <div id="home-keys"> | |
| <span>SCROLL to zoom</span> | |
| <span class="hk-dot">·</span> | |
| <span>DRAG to orbit</span> | |
| <span class="hk-dot">·</span> | |
| <span>CLICK to explore</span> | |
| <button class="tab-btn" data-tab="discover">DISCOVER.</button> | |
| </div> | |
| </div> | |
| <div id="home-credits">Engine Architecture · Neel, Ansh & Aarush</div> | |
| </div> | |
| <div id="top-bar"> | |
| <div id="brand"> | |
| <span class="brand-icon">�S�</span> | |
| <span class="brand-text">SOLAR ENGINE <span class="brand-ver">v3.3</span></span> | |
| </div> | |
| <div id="search-wrap"> | |
| <span class="search-icon">�R"</span> | |
| <input id="search-input" type="text" placeholder="Search planets, stars, anomalies⬦" autocomplete="off" spellcheck="false" /> | |
| <div id="search-results"></div> | |
| </div> | |
| <div id="time-controls"> | |
| <button class="tc-btn" id="btn-rewind" title="Slow down (-)">«</button> | |
| <button class="tc-btn" id="btn-pause" title="Pause / Resume (Space)">⏸</button> | |
| <button class="tc-btn" id="btn-forward" title="Speed up (+)">»</button> | |
| <span id="speed-display" title="Simulation speed">1000�</span> | |
| </div> | |
| <div id="top-right-btns"> | |
| <button class="icon-btn" id="btn-reset-cam" title="Reset camera (H)">�R</button> | |
| <button class="icon-btn" id="btn-toggle-orbits" title="Toggle orbits (O)">�`"</button> | |
| <button class="icon-btn" id="btn-random-fact" title="Random Discovery (D)">�x�</button> | |
| <button class="icon-btn" id="btn-compare" title="Compare two objects (C)">�a</button> | |
| <button class="icon-btn" id="btn-timeline" title="Cosmic Timeline (T)">�x"�</button> | |
| <button class="icon-btn" id="btn-settings" title="Settings">�a"</button> | |
| <button class="icon-btn" id="btn-shortcuts" title="Keyboard shortcuts (?)">?</button> | |
| <button class="icon-btn" id="btn-screenshot" title="Screenshot">⬡</button> | |
| <button class="icon-btn" id="btn-fullscreen" title="Fullscreen (F11)">⤢</button> | |
| </div> | |
| </div> | |
| <div id="favorites-bar" style="display: none"></div> | |
| <div id="scale-indicator"> | |
| <div id="scale-label">SOLAR SYSTEM</div> | |
| <div id="scale-bar-wrap"><div id="scale-bar-fill"></div></div> | |
| </div> | |
| <div id="settings-panel"> | |
| <div id="sp-header"> | |
| <span class="sp-title">�a" CONTROLS & SETTINGS</span> | |
| <button id="sp-close" onclick="toggleSettings(false)">�S"</button> | |
| </div> | |
| <div class="sp-section"> | |
| <div class="sp-section-label">MOUSE SENSITIVITY</div> | |
| <div class="sp-row"> | |
| <label class="sp-label">Rotate Speed</label> | |
| <div class="sp-slider-wrap"> | |
| <input type="range" class="sp-slider" id="sl-rotate" min="0.1" max="2" step="0.05" value="0.6" /> | |
| <span class="sp-val" id="sv-rotate">0.60</span> | |
| </div> | |
| </div> | |
| <div class="sp-row"> | |
| <label class="sp-label">Zoom Speed</label> | |
| <div class="sp-slider-wrap"> | |
| <input type="range" class="sp-slider" id="sl-zoom" min="0.2" max="3" step="0.1" value="1.2" /> | |
| <span class="sp-val" id="sv-zoom">1.20</span> | |
| </div> | |
| </div> | |
| <div class="sp-row"> | |
| <label class="sp-label">Pan Speed</label> | |
| <div class="sp-slider-wrap"> | |
| <input type="range" class="sp-slider" id="sl-pan" min="0.1" max="2" step="0.05" value="0.8" /> | |
| <span class="sp-val" id="sv-pan">0.80</span> | |
| </div> | |
| </div> | |
| <div class="sp-row"> | |
| <label class="sp-label">Damping</label> | |
| <div class="sp-slider-wrap"> | |
| <input type="range" class="sp-slider" id="sl-damp" min="0.02" max="0.2" step="0.01" value="0.08" /> | |
| <span class="sp-val" id="sv-damp">0.08</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="sp-section"> | |
| <div class="sp-section-label">DISPLAY</div> | |
| <div class="sp-row"> | |
| <label class="sp-label">Label Visibility</label> | |
| <div class="sp-slider-wrap"> | |
| <input type="range" class="sp-slider" id="sl-labels" min="0" max="1" step="0.05" value="1" /> | |
| <span class="sp-val" id="sv-labels">1.00</span> | |
| </div> | |
| </div> | |
| <div class="sp-row"> | |
| <label class="sp-label">Star Brightness</label> | |
| <div class="sp-slider-wrap"> | |
| <input type="range" class="sp-slider" id="sl-stars" min="0.3" max="1.5" step="0.05" value="0.9" /> | |
| <span class="sp-val" id="sv-stars">0.90</span> | |
| </div> | |
| </div> | |
| <div class="sp-row"> | |
| <label class="sp-label">Render Quality</label> | |
| <div class="sp-slider-wrap"> | |
| <input type="range" class="sp-slider" id="sl-quality" min="0.5" max="2" step="0.25" value="1.5" /> | |
| <span class="sp-val" id="sv-quality">1.50</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="sp-section"> | |
| <div class="sp-section-label">TOGGLES</div> | |
| <div class="sp-toggles"> | |
| <button class="sp-toggle active" id="tog-orbits" onclick="toggleOrbitsBtn(this)">ORBIT LINES</button> | |
| <button class="sp-toggle active" id="tog-labels" onclick="toggleLabelsBtn(this)">LABELS</button> | |
| <button class="sp-toggle active" id="tog-glow" onclick="toggleGlowBtn(this)">SUN GLOW</button> | |
| <button class="sp-toggle" id="tog-perf" onclick="togglePerfBtn(this)">PERF MODE</button> | |
| </div> | |
| </div> | |
| <button class="sp-reset" onclick="resetSettings()">� � RESET TO DEFAULTS</button> | |
| </div> | |
| <div id="shortcuts-overlay" onclick="if (event.target === this) closeShortcuts()"> | |
| <div id="shortcuts-modal"> | |
| <div id="sm-header"> | |
| <span>�R� KEYBOARD SHORTCUTS</span> | |
| <button onclick="closeShortcuts()">�S"</button> | |
| </div> | |
| <div id="sm-grid"> | |
| <div class="sm-group"> | |
| <div class="sm-group-title">NAVIGATION</div> | |
| <div class="sm-row"><kbd>H</kbd><span>Reset Camera</span></div> | |
| <div class="sm-row"><kbd>F11</kbd><span>Fullscreen</span></div> | |
| <div class="sm-row"><kbd>ESC</kbd><span>Exit fullscreen / close panels</span></div> | |
| <div class="sm-row"><kbd>R</kbd><span>Reload</span></div> | |
| <div class="sm-row"><kbd>M</kbd><span>Measure distance between two objects</span></div> | |
| <div class="sm-row"><kbd>F</kbd><span>Bookmark / favourite selected object</span></div> | |
| </div> | |
| <div class="sm-group"> | |
| <div class="sm-group-title">SIMULATION</div> | |
| <div class="sm-row"><kbd>Space</kbd><span>Pause / Resume</span></div> | |
| <div class="sm-row"><kbd>+</kbd><span>Speed up time</span></div> | |
| <div class="sm-row"><kbd>-</kbd><span>Slow down time</span></div> | |
| <div class="sm-row"><kbd>O</kbd><span>Toggle orbit lines</span></div> | |
| </div> | |
| <div class="sm-group"> | |
| <div class="sm-group-title">VIEW</div> | |
| <div class="sm-row"><kbd>?</kbd><span>This help panel</span></div> | |
| <div class="sm-row"><kbd>S</kbd><span>Settings panel</span></div> | |
| <div class="sm-row"><kbd>P</kbd><span>Screenshot</span></div> | |
| <div class="sm-row"><kbd>L</kbd><span>Toggle labels</span></div> | |
| </div> | |
| <div class="sm-group"> | |
| <div class="sm-group-title">MOUSE</div> | |
| <div class="sm-row"><kbd>LMB</kbd><span>Click to select object</span></div> | |
| <div class="sm-row"><kbd>RMB</kbd><span>Pan camera</span></div> | |
| <div class="sm-row"><kbd>Scroll</kbd><span>Zoom in / out</span></div> | |
| <div class="sm-row"><kbd>Drag</kbd><span>Orbit camera</span></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="hud"> | |
| <div id="hud-header"> | |
| <div> | |
| <div id="hud-type-badge">OBJECT</div> | |
| <h1 id="hud-title">�</h1> | |
| </div> | |
| <button id="close-btn" onclick="closeHUD()" title="Close">�S"</button> | |
| </div> | |
| <div id="hud-surface"> | |
| <img id="hud-img" src="" alt="Surface" loading="lazy" /> | |
| <div id="hud-img-overlay"></div> | |
| </div> | |
| <div id="quick-stats"> | |
| <div class="qs-pill"> | |
| <div class="qs-val" id="qs-dist">�</div> | |
| <div class="qs-lbl">DISTANCE AU</div> | |
| </div> | |
| <div class="qs-pill"> | |
| <div class="qs-val" id="qs-period">�</div> | |
| <div class="qs-lbl">PERIOD DAYS</div> | |
| </div> | |
| <div class="qs-pill"> | |
| <div class="qs-val" id="qs-moons">�</div> | |
| <div class="qs-lbl">MOONS</div> | |
| </div> | |
| <div class="qs-pill"> | |
| <div class="qs-val" id="qs-type">�</div> | |
| <div class="qs-lbl">CLASS</div> | |
| </div> | |
| </div> | |
| <div id="hud-tabs"> | |
| <button class="tab-btn active" data-tab="overview">OVERVIEW</button> | |
| <button class="tab-btn" data-tab="physics">PHYSICS</button> | |
| <button class="tab-btn" data-tab="atmosphere">ATMOS.</button> | |
| <button class="tab-btn" data-tab="explore">EXPLORE</button> | |
| <button class="tab-btn" data-tab="discover">DISCOVER.</button> | |
| <button class="tab-btn" data-tab="moons">MOONS</button> | |
| </div> | |
| <div id="hud-tab-body"> | |
| <div class="tab-pane active" id="tab-overview"> | |
| <p id="tab-overview-text" class="description"></p> | |
| <div id="tab-overview-fact" class="fun-fact-box"></div> | |
| </div> | |
| <div class="tab-pane" id="tab-physics"> | |
| <div id="tab-physics-rows"></div> | |
| </div> | |
| <div class="tab-pane" id="tab-atmosphere"> | |
| <div id="tab-atmos-rows"></div> | |
| </div> | |
| <div class="tab-pane" id="tab-explore"> | |
| <div id="tab-explore-rows"></div> | |
| </div> | |
| <div class="tab-pane" id="tab-discover"> | |
| <div id="tab-discover-rows"></div> | |
| </div> | |
| <div class="tab-pane" id="tab-moons"> | |
| <div id="tab-moons-rows"></div> | |
| </div> | |
| </div> | |
| <button id="focus-btn" onclick="focusSelectedPlanet()">�� FOCUS CAMERA</button> | |
| </div> | |
| <div id="status-bar"> | |
| <span id="sb-date">�</span> | |
| <span class="sb-sep">|</span> | |
| <span id="sb-fps">� FPS</span> | |
| <span class="sb-sep">|</span> | |
| <span id="sb-cam">CAM �</span> | |
| <span class="sb-sep">|</span> | |
| <span id="sb-selected">No object selected</span> | |
| <span class="sb-sep" style="margin-left: auto">|</span> | |
| <span id="sb-hint">Click object to explore · ? for shortcuts</span> | |
| </div> | |
| <div id="dev-credits">Engine Architecture · Neel, Ansh & Aarush</div> | |
| <div id="galaxy-label"> | |
| <div id="gl-name">MILKY WAY GALAXY</div> | |
| <div id="gl-sub">Diameter: ~105,700 light-years · ~400 billion stars</div> | |
| <div id="gl-you">��0 Our Solar System</div> | |
| </div> | |
| <div id="fact-toast"> | |
| <div id="fact-toast-header"> | |
| <span>�x� RANDOM DISCOVERY</span> | |
| <button onclick="document.getElementById('fact-toast').classList.remove('active')">�S"</button> | |
| </div> | |
| <div id="fact-toast-object"></div> | |
| <div id="fact-toast-text"></div> | |
| </div> | |
| <div id="compare-panel"> | |
| <div id="cp-header"> | |
| <span>�a OBJECT COMPARISON</span> | |
| <button onclick="closeCompare()">�S"</button> | |
| </div> | |
| <div id="cp-hint">Click any object in the scene to add it to the comparison (max 2).</div> | |
| <div id="cp-body"> | |
| <div class="cp-col" id="cp-col-a"> | |
| <div class="cp-name">�</div> | |
| <div class="cp-rows"></div> | |
| </div> | |
| <div id="cp-divider">VS</div> | |
| <div class="cp-col" id="cp-col-b"> | |
| <div class="cp-name">�</div> | |
| <div class="cp-rows"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="timeline-panel"> | |
| <div id="tp-header"> | |
| <span>�x"� COSMIC TIMELINE & SCALE</span> | |
| <button onclick="document.getElementById('timeline-panel').classList.remove('active')">�S"</button> | |
| </div> | |
| <div id="tp-body"> | |
| <div class="tp-scale-title">THE COSMIC SCALE</div> | |
| <div class="tp-scale-row"><span class="tp-icon">�x�</span><span class="tp-label">SUBATOMIC</span><span class="tp-val">~10⁻¹⁵ m � Quarks, protons, neutrons</span></div> | |
| <div class="tp-scale-row"><span class="tp-icon">�a:</span><span class="tp-label">ATOMIC</span><span class="tp-val">~10⁻¹⁰ m � Atoms, molecules</span></div> | |
| <div class="tp-scale-row"><span class="tp-icon">�x��</span><span class="tp-label">BIOLOGICAL</span><span class="tp-val">~10⁻⁶ m � Cells, organisms</span></div> | |
| <div class="tp-scale-row tp-highlight"><span class="tp-icon">�xR�</span><span class="tp-label">PLANETARY</span><span class="tp-val">~10⁷ m � Moons, planets, dwarf planets</span></div> | |
| <div class="tp-scale-row tp-highlight"><span class="tp-icon">�ܬ</span><span class="tp-label">STELLAR</span><span class="tp-val">~10⁹ m � Stars, star systems, nebulae</span></div> | |
| <div class="tp-scale-row tp-highlight"><span class="tp-icon">�xRR</span><span class="tp-label">GALACTIC</span><span class="tp-val">~10²⁰ m � Galaxies (Milky Way: 10²¹ m)</span></div> | |
| <div class="tp-scale-row"><span class="tp-icon">�x"�</span><span class="tp-label">SUPERCLUSTER</span><span class="tp-val">~10²³ m � Laniakea: 520M light-years</span></div> | |
| <div class="tp-scale-row"><span class="tp-icon">��~</span><span class="tp-label">OBSERVABLE UNIVERSE</span><span class="tp-val">~8.8�10²⁶ m � 93 billion light-years</span></div> | |
| <div class="tp-divider"></div> | |
| <div class="tp-scale-title">UNIVERSE HISTORY</div> | |
| <div class="tp-time-row"><span class="tp-time">t = 0</span><span class="tp-event">Big Bang � all energy in a single point</span></div> | |
| <div class="tp-time-row"><span class="tp-time">10⁻³⁵ s</span><span class="tp-event">Cosmic Inflation � universe expands faster than light</span></div> | |
| <div class="tp-time-row"><span class="tp-time">3 min</span><span class="tp-event">Big Bang Nucleosynthesis � H, He, Li nuclei form</span></div> | |
| <div class="tp-time-row"><span class="tp-time">380,000 yr</span><span class="tp-event">Recombination � CMB released; universe becomes transparent</span></div> | |
| <div class="tp-time-row"><span class="tp-time">200M yr</span><span class="tp-event">First Stars (Population III) ignite � end of the Cosmic Dark Ages</span></div> | |
| <div class="tp-time-row"><span class="tp-time">1B yr</span><span class="tp-event">First galaxies form; quasars at peak activity</span></div> | |
| <div class="tp-time-row"><span class="tp-time">4.6B yr</span><span class="tp-event">Milky Way disk forms; Population II stars present</span></div> | |
| <div class="tp-time-row"><span class="tp-time">9.2B yr</span><span class="tp-event">Solar System forms from molecular cloud collapse</span></div> | |
| <div class="tp-time-row"><span class="tp-time">9.6B yr</span><span class="tp-event">Earth forms; Moon-forming giant impact ~30M yr later</span></div> | |
| <div class="tp-time-row"><span class="tp-time">10.1B yr</span><span class="tp-event">First life on Earth � simple prokaryotes</span></div> | |
| <div class="tp-time-row"><span class="tp-time">13.5B yr</span><span class="tp-event">Homo sapiens evolve; first astronomy begins</span></div> | |
| <div class="tp-time-row tp-now"><span class="tp-time">13.8B yr</span><span class="tp-event">⬤ NOW � You are here, reading this</span></div> | |
| <div class="tp-time-row"><span class="tp-time">+4.5B yr</span><span class="tp-event">Andromeda-Milky Way collision begins</span></div> | |
| <div class="tp-time-row"><span class="tp-time">+5B yr</span><span class="tp-event">Sun becomes red giant; Earth's surface sterilized</span></div> | |
| <div class="tp-time-row"><span class="tp-time">+10¹⁴ yr</span><span class="tp-event">Last stars exhaust fuel; universe goes dark</span></div> | |
| <div class="tp-time-row"><span class="tp-time">+10¹⁰⁰ yr</span><span class="tp-event">Proton decay � all ordinary matter gone</span></div> | |
| </div> | |
| </div> | |
| <div id="screenshot-flash"></div> | |
| <script id="patch-fullscreen"> | |
| document.getElementById('btn-fullscreen').addEventListener('click', () => { | |
| try { | |
| if (document.fullscreenElement) { | |
| document.exitFullscreen().catch(() => {}) | |
| } else { | |
| document.documentElement.requestFullscreen().catch(() => { | |
| // Running inside Qt � fullscreen is owned by the native window (F11). | |
| }) | |
| } | |
| } catch (e) { | |
| /* Qt WebEngine does not support the Fullscreen API */ | |
| } | |
| }) | |
| </script> | |
| <script id="patch-hud-image"> | |
| /** | |
| * Generates a 400�160 canvas thumbnail for any celestial object. | |
| * Used by openHUD when data.texture is absent. | |
| */ | |
| function generateHudImage(data) { | |
| const W = 400, | |
| H = 160 | |
| const cvs = document.createElement('canvas') | |
| cvs.width = W | |
| cvs.height = H | |
| const ctx = cvs.getContext('2d') | |
| // Background | |
| ctx.fillStyle = '#060810' | |
| ctx.fillRect(0, 0, W, H) | |
| // Colour from hex int | |
| const col = data.color || 0x4488ff | |
| const r = (col >> 16) & 0xff | |
| const g = (col >> 8) & 0xff | |
| const b = col & 0xff | |
| // Radial colour glow | |
| const grd = ctx.createRadialGradient(W / 2, H / 2, 0, W / 2, H / 2, H * 0.85) | |
| grd.addColorStop(0, `rgba(${r},${g},${b},0.60)`) | |
| grd.addColorStop(0.45, `rgba(${r},${g},${b},0.14)`) | |
| grd.addColorStop(1, 'rgba(0,0,0,0)') | |
| ctx.fillStyle = grd | |
| ctx.fillRect(0, 0, W, H) | |
| // Subtle star field | |
| for (let i = 0; i < 80; i++) { | |
| ctx.fillStyle = `rgba(200,215,255,${(Math.random() * 0.28 + 0.04).toFixed(2)})` | |
| ctx.fillRect((Math.random() * W) | 0, (Math.random() * H) | 0, 1, 1) | |
| } | |
| // Type icon � centred | |
| const icons = { | |
| star: '�ܬ', | |
| planet: '�0', | |
| dwarf: '�}', | |
| comet: '��', | |
| probe: '⬡', | |
| blackhole: '⬤', | |
| pulsar: '�`"', | |
| galaxy: '�S�', | |
| interstellar: '� ' | |
| } | |
| const icon = icons[data.type] || '�0' | |
| ctx.font = `bold ${Math.round(H * 0.44)}px Arial, sans-serif` | |
| ctx.textAlign = 'center' | |
| ctx.textBaseline = 'middle' | |
| ctx.globalAlpha = 0.52 | |
| ctx.fillStyle = `rgb(${r},${g},${b})` | |
| ctx.fillText(icon, W / 2, H / 2) | |
| ctx.globalAlpha = 1 | |
| // Horizontal accent line | |
| ctx.strokeStyle = `rgba(${r},${g},${b},0.28)` | |
| ctx.lineWidth = 1 | |
| ctx.beginPath() | |
| ctx.moveTo(W * 0.12, H - 42) | |
| ctx.lineTo(W * 0.88, H - 42) | |
| ctx.stroke() | |
| // Type badge text | |
| ctx.fillStyle = `rgb(${r},${g},${b})` | |
| ctx.font = `10px 'Space Mono', monospace` | |
| ctx.textBaseline = 'bottom' | |
| ctx.fillText(data.typeBadge || (data.type || 'OBJECT').toUpperCase(), W / 2, H - 30) | |
| // Object name | |
| ctx.fillStyle = 'rgba(255,255,255,0.88)' | |
| ctx.font = `bold 15px 'Exo 2', Arial, sans-serif` | |
| ctx.fillText((data.name || '').toUpperCase(), W / 2, H - 10) | |
| return cvs.toDataURL('image/png') | |
| } | |
| </script> | |
| <script> | |
| /* �"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"� | |
| UI CONTROLLER | |
| �"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"��"� */ | |
| // ���� Homepage �������������������������������������������������������������������������������������������� | |
| let appEntered = false | |
| function enterApp() { | |
| if (appEntered) return | |
| appEntered = true | |
| const hp = document.getElementById('homepage') | |
| hp.classList.add('exiting') | |
| setTimeout(() => { | |
| hp.style.display = 'none' | |
| }, 900) | |
| } | |
| // Homepage canvas star animation | |
| ;(function () { | |
| const canvas = document.getElementById('home-canvas') | |
| const ctx = canvas.getContext('2d') | |
| let W, | |
| H, | |
| stars = [], | |
| animId | |
| function resize() { | |
| W = canvas.width = window.innerWidth | |
| H = canvas.height = window.innerHeight | |
| } | |
| function initStars() { | |
| stars = [] | |
| const N = Math.min(Math.floor((W * H) / 3000), 500) | |
| for (let i = 0; i < N; i++) { | |
| stars.push({ | |
| x: Math.random() * W, | |
| y: Math.random() * H, | |
| r: Math.random() * 1.4 + 0.2, | |
| a: Math.random(), | |
| da: (Math.random() * 0.4 + 0.1) * (Math.random() < 0.5 ? 1 : -1), | |
| dx: (Math.random() - 0.5) * 0.12, | |
| dy: (Math.random() - 0.5) * 0.12 | |
| }) | |
| } | |
| } | |
| function draw() { | |
| ctx.clearRect(0, 0, W, H) | |
| const t = performance.now() * 0.001 | |
| for (const s of stars) { | |
| s.a += s.da * 0.01 | |
| if (s.a > 1 || s.a < 0) s.da *= -1 | |
| s.x += s.dx | |
| s.y += s.dy | |
| if (s.x < 0) s.x = W | |
| if (s.x > W) s.x = 0 | |
| if (s.y < 0) s.y = H | |
| if (s.y > H) s.y = 0 | |
| ctx.beginPath() | |
| ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2) | |
| ctx.fillStyle = `rgba(200,215,255,${Math.max(0.05, Math.abs(s.a))})` | |
| ctx.fill() | |
| } | |
| animId = requestAnimationFrame(draw) | |
| } | |
| window.addEventListener('resize', () => { | |
| resize() | |
| initStars() | |
| }) | |
| resize() | |
| initStars() | |
| draw() | |
| // Stop animation when homepage hidden | |
| document.getElementById('launch-btn').addEventListener('click', () => { | |
| setTimeout(() => cancelAnimationFrame(animId), 1000) | |
| }) | |
| })() | |
| // Stat counters on homepage | |
| ;(function () { | |
| const items = document.querySelectorAll('.hs-item') | |
| items.forEach((el) => { | |
| const cnt = el.dataset.count | |
| const lbl = el.dataset.label | |
| const sfx = el.dataset.suffix || '' | |
| if (cnt === '��~') { | |
| el.innerHTML = `<div class="hs-num">��~</div><div class="hs-label">${lbl}</div>` | |
| return | |
| } | |
| let cur = 0 | |
| const target = parseInt(cnt) | |
| const duration = 1400 | |
| const start = performance.now() | |
| el.innerHTML = `<div class="hs-num">0${sfx}</div><div class="hs-label">${lbl}</div>` | |
| function tick(now) { | |
| const p = Math.min((now - start) / duration, 1) | |
| const ease = 1 - Math.pow(1 - p, 3) | |
| el.querySelector('.hs-num').textContent = Math.floor(ease * target) + sfx | |
| if (p < 1) requestAnimationFrame(tick) | |
| else el.querySelector('.hs-num').textContent = target + sfx | |
| } | |
| setTimeout(() => requestAnimationFrame(tick), 400) | |
| }) | |
| })() | |
| // ���� Tab switching ������������������������������������������������������������������������������������ | |
| document.querySelectorAll('.tab-btn').forEach((btn) => { | |
| btn.addEventListener('click', () => { | |
| document.querySelectorAll('.tab-btn').forEach((b) => b.classList.remove('active')) | |
| document.querySelectorAll('.tab-pane').forEach((p) => p.classList.remove('active')) | |
| btn.classList.add('active') | |
| document.getElementById('tab-' + btn.dataset.tab).classList.add('active') | |
| }) | |
| }) | |
| function closeHUD() { | |
| document.getElementById('hud').classList.remove('active') | |
| document.getElementById('sb-selected').textContent = 'No object selected' | |
| } | |
| function resetHUDTabs() { | |
| document.querySelectorAll('.tab-btn').forEach((b, i) => b.classList.toggle('active', i === 0)) | |
| document.querySelectorAll('.tab-pane').forEach((p, i) => p.classList.toggle('active', i === 0)) | |
| } | |
| // ���� HUD population ���������������������������������������������������������������������������������� | |
| window.openHUD = function (data) { | |
| resetHUDTabs() | |
| document.getElementById('hud-title').textContent = data.name | |
| document.getElementById('hud-type-badge').textContent = data.typeBadge || data.type?.toUpperCase() || 'OBJECT' | |
| document.getElementById('sb-selected').textContent = '�S� ' + data.name | |
| const AU = (data.dist / 149.6).toFixed(2) | |
| document.getElementById('qs-dist').textContent = data.dist > 0 ? AU : '0 (origin)' | |
| document.getElementById('qs-period').textContent = data.period > 0 ? Number(data.period).toLocaleString() : '�' | |
| document.getElementById('qs-moons').textContent = data.moons || '0' | |
| document.getElementById('qs-type').textContent = (data.typeBadge || data.type || '�').split(' ')[0] | |
| const img = document.getElementById('hud-img') | |
| img.style.opacity = 0 | |
| if (data.texture && data.texture !== '') { | |
| img.onerror = () => { | |
| // File missing � fall back to procedural thumbnail | |
| img.onerror = null | |
| img.src = generateHudImage(data) | |
| img.style.opacity = 1 | |
| } | |
| setTimeout(() => { | |
| img.src = data.texture | |
| img.style.opacity = 1 | |
| }, 180) | |
| } else { | |
| img.src = generateHudImage(data) | |
| img.style.opacity = 1 | |
| } | |
| document.getElementById('tab-overview-text').textContent = data.overview || data.desc || '�' | |
| const factBox = document.getElementById('tab-overview-fact') | |
| factBox.style.display = data.funFact ? 'block' : 'none' | |
| factBox.textContent = data.funFact ? '�a� ' + data.funFact : '' | |
| document.getElementById('tab-physics-rows').innerHTML = buildRows(data.stats) | |
| document.getElementById('tab-atmos-rows').innerHTML = buildRows(data.atmosphere) | |
| document.getElementById('tab-explore-rows').innerHTML = buildRows(data.exploration) | |
| document.getElementById('tab-discover-rows').innerHTML = buildRows(data.discoveries) | |
| document.getElementById('tab-moons-rows').innerHTML = buildMoonsTab(data.name) | |
| document.getElementById('hud').classList.add('active') | |
| } | |
| function buildRows(obj) { | |
| if (!obj) return '<p class="no-data">No data available.</p>' | |
| if (typeof obj === 'string') return `<p class="description">${obj}</p>` | |
| const entries = Object.entries(obj) | |
| if (!entries.length) return '<p class="no-data">No data available.</p>' | |
| return entries.map(([k, v]) => `<div class="data-row"><span class="label">${k}</span><span class="value">${v}</span></div>`).join('') | |
| } | |
| function buildMoonsTab(parentName) { | |
| const children = Object.entries(window.planetData || {}).filter(([, d]) => d.parent === parentName) | |
| if (!children.length) return '<p class="no-moons-msg">No major moon data on record for this object.</p>' | |
| return children | |
| .map(([name, d]) => { | |
| const period = d.period > 0 ? d.period.toFixed(d.period < 10 ? 2 : 0) + (d.period < 2 ? ' Earth days' : ' Earth days') : '�' | |
| const dia = (d.stats && (d.stats['Diameter'] || d.stats['Dimensions'])) || '�' | |
| const summary = d.overview ? d.overview.slice(0, 120) + '⬦' : '�' | |
| return `<div class="moon-entry"> | |
| <span class="moon-entry-name" onclick="if(window._searchSelect)window._searchSelect('${name}')">${name}</span> | |
| <span class="moon-entry-badge">${d.typeBadge || 'MOON'}</span> | |
| <div class="moon-entry-facts"> | |
| <b>Diameter:</b> ${dia} · <b>Period:</b> ${period}<br> | |
| ${summary} | |
| </div> | |
| </div>` | |
| }) | |
| .join('') | |
| } | |
| window.focusSelectedPlanet = function () { | |
| if (window._focusCallback) window._focusCallback() | |
| } | |
| // ���� Settings panel ���������������������������������������������������������������������������������� | |
| let settingsOpen = false | |
| window.toggleSettings = function (force) { | |
| settingsOpen = force !== undefined ? force : !settingsOpen | |
| document.getElementById('settings-panel').classList.toggle('active', settingsOpen) | |
| } | |
| document.getElementById('btn-settings').addEventListener('click', () => toggleSettings()) | |
| function setupSlider(id, valId, type, fmt) { | |
| const sl = document.getElementById(id) | |
| const vl = document.getElementById(valId) | |
| sl.addEventListener('input', () => { | |
| const v = parseFloat(sl.value) | |
| vl.textContent = fmt ? v.toFixed(fmt) : v | |
| if (window.setSensitivity) window.setSensitivity(type, v) | |
| }) | |
| } | |
| setupSlider('sl-rotate', 'sv-rotate', 'rotate', 2) | |
| setupSlider('sl-zoom', 'sv-zoom', 'zoom', 2) | |
| setupSlider('sl-pan', 'sv-pan', 'pan', 2) | |
| setupSlider('sl-damp', 'sv-damp', 'damp', 2) | |
| setupSlider('sl-labels', 'sv-labels', 'labels', 2) | |
| setupSlider('sl-stars', 'sv-stars', 'stars', 2) | |
| setupSlider('sl-quality', 'sv-quality', 'quality', 2) | |
| window.resetSettings = function () { | |
| const defaults = { 'sl-rotate': 0.6, 'sl-zoom': 1.2, 'sl-pan': 0.8, 'sl-damp': 0.08, 'sl-labels': 1, 'sl-stars': 0.9, 'sl-quality': 1.5 } | |
| const vals = { 'sl-rotate': 'sv-rotate', 'sl-zoom': 'sv-zoom', 'sl-pan': 'sv-pan', 'sl-damp': 'sv-damp', 'sl-labels': 'sv-labels', 'sl-stars': 'sv-stars', 'sl-quality': 'sv-quality' } | |
| const types = { 'sl-rotate': 'rotate', 'sl-zoom': 'zoom', 'sl-pan': 'pan', 'sl-damp': 'damp', 'sl-labels': 'labels', 'sl-stars': 'stars', 'sl-quality': 'quality' } | |
| Object.entries(defaults).forEach(([id, val]) => { | |
| const el = document.getElementById(id) | |
| el.value = val | |
| document.getElementById(vals[id]).textContent = val.toFixed(2) | |
| if (window.setSensitivity) window.setSensitivity(types[id], val) | |
| }) | |
| } | |
| // Toggle buttons in settings | |
| window.toggleOrbitsBtn = function (btn) { | |
| btn.classList.toggle('active') | |
| document.getElementById('btn-toggle-orbits').click() | |
| } | |
| window.toggleLabelsBtn = function (btn) { | |
| btn.classList.toggle('active') | |
| if (window.setSensitivity) window.setSensitivity('labels', btn.classList.contains('active') ? 1 : 0) | |
| } | |
| window.toggleGlowBtn = function (btn) { | |
| btn.classList.toggle('active') | |
| if (window.setSensitivity) window.setSensitivity('glow', btn.classList.contains('active') ? 1 : 0) | |
| } | |
| window.togglePerfBtn = function (btn) { | |
| btn.classList.toggle('active') | |
| if (window.setSensitivity) window.setSensitivity('perfMode', btn.classList.contains('active') ? 1 : 0) | |
| } | |
| // ���� Shortcuts �������������������������������������������������������������������������������������������� | |
| window.closeShortcuts = function () { | |
| document.getElementById('shortcuts-overlay').classList.remove('active') | |
| } | |
| document.getElementById('btn-shortcuts').addEventListener('click', () => { | |
| document.getElementById('shortcuts-overlay').classList.toggle('active') | |
| }) | |
| // ���� Screenshot ������������������������������������������������������������������������������������������ | |
| document.getElementById('btn-screenshot').addEventListener('click', doScreenshot) | |
| function doScreenshot() { | |
| if (!window._screenshotCallback) return | |
| window._screenshotCallback() | |
| const flash = document.getElementById('screenshot-flash') | |
| flash.classList.add('active') | |
| setTimeout(() => flash.classList.remove('active'), 300) | |
| } | |
| // ���� Orbit / Reset / Fullscreen ���������������������������������������������������������� | |
| document.getElementById('btn-toggle-orbits').addEventListener('click', function () { | |
| this.classList.toggle('active') | |
| const hide = this.classList.contains('active') | |
| if (window._setOrbitsVisible) window._setOrbitsVisible(!hide) | |
| }) | |
| document.getElementById('btn-reset-cam').addEventListener('click', () => { | |
| if (window._resetCamera) window._resetCamera() | |
| }) | |
| // ���� Time controls ������������������������������������������������������������������������������������ | |
| document.getElementById('btn-pause').addEventListener('click', () => { | |
| if (window.togglePause) window.togglePause() | |
| }) | |
| document.getElementById('btn-forward').addEventListener('click', () => { | |
| if (window.speedUp) window.speedUp() | |
| }) | |
| document.getElementById('btn-rewind').addEventListener('click', () => { | |
| if (window.speedDown) window.speedDown() | |
| }) | |
| // ���� Search �������������������������������������������������������������������������������������������������� | |
| const searchInput = document.getElementById('search-input') | |
| const searchResults = document.getElementById('search-results') | |
| searchInput.addEventListener('keydown', (e) => { | |
| if (e.key === 'Escape') { | |
| searchInput.value = '' | |
| searchResults.innerHTML = '' | |
| searchInput.blur() | |
| } | |
| }) | |
| searchResults.addEventListener('click', (e) => { | |
| const item = e.target.closest('.sr-item') | |
| if (!item) return | |
| if (window._searchSelect) window._searchSelect(item.dataset.name) | |
| searchInput.value = '' | |
| searchResults.innerHTML = '' | |
| }) | |
| document.addEventListener('click', (e) => { | |
| if (!e.target.closest('#search-wrap')) { | |
| searchResults.innerHTML = '' | |
| } | |
| }) | |
| // ���� Random Fact ������������������������������������������������������������������������ | |
| document.getElementById('btn-random-fact').addEventListener('click', showRandomFact) | |
| function showRandomFact() { | |
| if (window._showRandomFact) window._showRandomFact() | |
| } | |
| // ���� Compare mode ���������������������������������������������������������������������� | |
| let compareMode = false | |
| window.openCompare = function () { | |
| compareMode = true | |
| document.getElementById('compare-panel').classList.add('active') | |
| document.getElementById('cp-hint').style.display = 'block' | |
| } | |
| window.closeCompare = function () { | |
| compareMode = false | |
| document.getElementById('compare-panel').classList.remove('active') | |
| if (window._clearCompare) window._clearCompare() | |
| } | |
| window.isCompareMode = function () { | |
| return compareMode | |
| } | |
| document.getElementById('btn-compare').addEventListener('click', () => { | |
| if (document.getElementById('compare-panel').classList.contains('active')) closeCompare() | |
| else window.openCompare() | |
| }) | |
| // ���� Cosmic Timeline ���������������������������������������������������������������� | |
| document.getElementById('btn-timeline').addEventListener('click', () => { | |
| document.getElementById('timeline-panel').classList.toggle('active') | |
| }) | |
| // ���� Keyboard handling ������������������������������������������������������������ | |
| document.addEventListener('keydown', (e) => { | |
| if (e.target.tagName === 'INPUT') return | |
| const k = e.key | |
| if (k === 'h' || k === 'H') { | |
| if (window._resetCamera) window._resetCamera() | |
| } | |
| if (k === 'o' || k === 'O') { | |
| document.getElementById('btn-toggle-orbits').click() | |
| } | |
| if (k === '?') { | |
| document.getElementById('shortcuts-overlay').classList.toggle('active') | |
| } | |
| if (k === 's' || k === 'S') { | |
| toggleSettings() | |
| } | |
| if (k === 'p' || k === 'P') { | |
| doScreenshot() | |
| } | |
| if (k === 'l' || k === 'L') { | |
| document.getElementById('tog-labels').click() | |
| } | |
| if (k === 'Escape') { | |
| closeShortcuts() | |
| toggleSettings(false) | |
| if (!document.fullscreenElement) closeHUD() | |
| } | |
| if (k === ' ') { | |
| e.preventDefault() | |
| if (window.togglePause) window.togglePause() | |
| } | |
| if (k === '+' || k === '=') { | |
| if (window.speedUp) window.speedUp() | |
| } | |
| if (k === '-') { | |
| if (window.speedDown) window.speedDown() | |
| } | |
| if (k === 'd' || k === 'D') showRandomFact() | |
| if (k === 'c' || k === 'C') { | |
| if (document.getElementById('compare-panel').classList.contains('active')) closeCompare() | |
| else window.openCompare() | |
| } | |
| if (k === 't' || k === 'T') document.getElementById('timeline-panel').classList.toggle('active') | |
| }) | |
| </script> | |
| <script defer type="module" src="logic.js"></script> | |
| <footer class="sf"> | |
| <div class="fi"> | |
| <div class="fb"><span>CoL</span> · Class Of Learners</div> | |
| <div class="fl"><a href="privacy">Privacy</a><a href="terms">Terms</a><a href="feedback">Feedback</a><a href="download">Downloads</a></div> | |
| <div class="fc">© 2026 Neel, Ansh, Aarush & Yashan.</div> | |
| </div> | |
| </footer> | |
| </body> | |
| </html> | |