Current Date:

1x

Loading Solar System...

// Planetary data with scientific accuracy but visual adjustments for better display const planetaryData = { sun: { name: "Sun", radius: 696340, // km mass: 1.989e30, // kg color: 0xffcc00, info: { type: "Star (G-type main-sequence)", diameter: "1,392,684 km", mass: "1.989 × 10^30 kg", surfaceTemp: "5,500°C (surface)", rotationPeriod: "25-35 days", distanceFromEarth: "149.6 million km", composition: "Hydrogen (73%), Helium (25%), other elements (2%)" } }, mercury: { name: "Mercury", radius: 2439.7, // km semiMajorAxis: 0.387 * 149597870.7, // AU to km eccentricity: 0.2056, inclination: 7.0, // degrees orbitalPeriod: 87.97, // days color: 0xc8c8c8, info: { type: "Terrestrial Planet", diameter: "4,879.4 km", mass: "3.3011 × 10^23 kg", surfaceTemp: "-173°C to 427°C", rotationPeriod: "58.65 days", orbitalPeriod: "87.97 days", distanceFromSun: "57.91 million km", atmosphere: "Minimal - sodium, potassium, oxygen" } }, venus: { name: "Venus", radius: 6051.8, // km semiMajorAxis: 0.723 * 149597870.7, // AU to km eccentricity: 0.0068, inclination: 3.39, // degrees orbitalPeriod: 224.7, // days color: 0xffd699, info: { type: "Terrestrial Planet", diameter: "12,104 km", mass: "4.8675 × 10^24 kg", surfaceTemp: "462°C (average)", rotationPeriod: "243 days (retrograde)", orbitalPeriod: "224.7 days", distanceFromSun: "108.2 million km", atmosphere: "Dense CO2, nitrogen, sulfuric acid clouds" } }, earth: { name: "Earth", radius: 6371, // km semiMajorAxis: 1.0 * 149597870.7, // AU to km eccentricity: 0.0167, inclination: 0.0, // degrees (reference plane) orbitalPeriod: 365.25, // days color: 0x4da6ff, info: { type: "Terrestrial Planet", diameter: "12,742 km", mass: "5.972 × 10^24 kg", surfaceTemp: "15°C (average)", rotationPeriod: "23.93 hours", orbitalPeriod: "365.25 days", distanceFromSun: "149.6 million km", atmosphere: "Nitrogen (78%), Oxygen (21%), other gases (1%)", moons: "1 (Luna)" } }, mars: { name: "Mars", radius: 3389.5, // km semiMajorAxis: 1.524 * 149597870.7, // AU to km eccentricity: 0.0934, inclination: 1.85, // degrees orbitalPeriod: 686.98, // days color: 0xff6b4a, info: { type: "Terrestrial Planet", diameter: "6,779 km", mass: "6.4171 × 10^23 kg", surfaceTemp: "-87°C to -5°C", rotationPeriod: "24.62 hours", orbitalPeriod: "687 days", distanceFromSun: "227.9 million km", atmosphere: "Thin - CO2 (95%), nitrogen, argon", moons: "2 (Phobos, Deimos)" } }, jupiter: { name: "Jupiter", radius: 69911, // km semiMajorAxis: 5.204 * 149597870.7, // AU to km eccentricity: 0.0489, inclination: 1.31, // degrees orbitalPeriod: 4332.59, // days color: 0xffb347, info: { type: "Gas Giant", diameter: "139,820 km", mass: "1.898 × 10^27 kg", surfaceTemp: "-145°C (cloud top)", rotationPeriod: "9.93 hours", orbitalPeriod: "11.86 years", distanceFromSun: "778.5 million km", atmosphere: "Hydrogen, helium, methane, ammonia", moons: "79+ known moons" } }, saturn: { name: "Saturn", radius: 58232, // km semiMajorAxis: 9.582 * 149597870.7, // AU to km eccentricity: 0.0565, inclination: 2.49, // degrees orbitalPeriod: 10759.22, // days color: 0xffd700, hasRings: true, info: { type: "Gas Giant", diameter: "116,460 km", mass: "5.683 × 10^26 kg", surfaceTemp: "-178°C (cloud top)", rotationPeriod: "10.7 hours", orbitalPeriod: "29.46 years", distanceFromSun: "1.43 billion km", atmosphere: "Hydrogen, helium, methane", rings: "Extensive ring system (ice particles)", moons: "82+ known moons" } }, uranus: { name: "Uranus", radius: 25362, // km semiMajorAxis: 19.201 * 149597870.7, // AU to km eccentricity: 0.0457, inclination: 0.77, // degrees orbitalPeriod: 30688.5, // days color: 0x40e0d0, info: { type: "Ice Giant", diameter: "50,724 km", mass: "8.681 × 10^25 kg", surfaceTemp: "-224°C (cloud top)", rotationPeriod: "17.24 hours (retrograde)", orbitalPeriod: "84.01 years", distanceFromSun: "2.87 billion km", atmosphere: "Hydrogen, helium, methane (gives blue color)", moons: "27 known moons" } }, neptune: { name: "Neptune", radius: 24622, // km semiMajorAxis: 30.047 * 149597870.7, // AU to km eccentricity: 0.0113, inclination: 1.77, // degrees orbitalPeriod: 60195, // days color: 0x4169e1, info: { type: "Ice Giant", diameter: "49,244 km", mass: "1.024 × 10^26 kg", surfaceTemp: "-218°C (cloud top)", rotationPeriod: "16.11 hours", orbitalPeriod: "164.8 years", distanceFromSun: "4.5 billion km", atmosphere: "Hydrogen, helium, methane (gives blue color)", moons: "14 known moons" } } }; // Scale factors for visualization - adjusted for better visibility const SCALE_FACTOR = 1000; // Much smaller scale factor to bring planets closer const SIZE_SCALE = 2000; // Much larger size scale for better visibility const SUN_SCALE = 0.8; // Smaller sun for better balance // Spacing adjustment factors for more balanced view - more dramatic spacing const SPACING_ADJUSTMENT = { mercury: 1.2, venus: 2, earth: 3, mars: 4, jupiter: 5.5, saturn: 7, uranus: 8.5, neptune: 10 }; // Global variables let scene, camera, renderer, controls; let planets = {}; let planetLabels = {}; let orbits = {}; let clock = new THREE.Clock(); let timeSpeed = 1; // Days per second let isPlaying = true; let showOrbits = true; // Show orbits by default let showLabels = true; // Show labels by default let currentDate = new Date(); let startDate = new Date(); let elapsedDays = 0; let raycaster = new THREE.Raycaster(); let mouse = new THREE.Vector2(); let hoveredPlanet = null; let assetsLoaded = 0; let totalAssets = 10; // Sun + 8 planets + starfield // Initialize the scene function init() { updateLoadingProgress(10); // Create scene scene = new THREE.Scene(); scene.background = new THREE.Color(0x000033); // Dark blue background for space // Create camera camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 50000); camera.position.set(0, 300, 500); // Better initial view to see all planets // Create renderer renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); document.getElementById('canvas-container').appendChild(renderer.domElement); updateLoadingProgress(20); // Add orbit controls controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.05; controls.maxDistance = 5000; // Limit how far users can zoom out // Add ambient light const ambientLight = new THREE.AmbientLight(0x888888); // Brighter ambient light scene.add(ambientLight); updateLoadingProgress(30); // Create starfield background createStarfield(); updateLoadingProgress(50); // Create sun with point light createSun(); updateLoadingProgress(60); // Create planets createPlanets(); updateLoadingProgress(80); // Add event listeners window.addEventListener('resize', onWindowResize); window.addEventListener('mousemove', onMouseMove); document.getElementById('play-pause').addEventListener('click', togglePlayPause); document.getElementById('speed-slider').addEventListener('input', updateSpeed); document.getElementById('go-to-date').addEventListener('click', goToDate); document.getElementById('reset-date').addEventListener('click', resetDate); document.getElementById('toggle-orbits').addEventListener('click', toggleOrbits); document.getElementById('toggle-labels').addEventListener('click', toggleLabels); document.getElementById('reset-view').addEventListener('click', resetView); // Initialize date picker with today's date const today = new Date(); const dateString = today.toISOString().split('T')[0]; document.getElementById('date-picker').value = dateString; updateLoadingProgress(90); // Start animation loop animate(); } function assetLoaded() { assetsLoaded++; const progress = Math.floor((assetsLoaded / totalAssets) * 100); updateLoadingProgress(progress); if (assetsLoaded >= totalAssets) { // Hide loading screen after a short delay setTimeout(() => { document.getElementById('loading-screen').style.display = 'none'; }, 500); } } function updateLoadingProgress(percent) { document.getElementById('loading-bar').style.width = `${percent}%`; } function createStarfield() { const starGeometry = new THREE.BufferGeometry(); const starCount = 2000; const positions = new Float32Array(starCount * 3); const sizes = new Float32Array(starCount); for (let i = 0; i < starCount * 3; i += 3) { // Random position in a sphere const radius = 4000; const theta = 2 * Math.PI * Math.random(); const phi = Math.acos(2 * Math.random() - 1); positions[i] = radius * Math.sin(phi) * Math.cos(theta); positions[i + 1] = radius * Math.sin(phi) * Math.sin(theta); positions[i + 2] = radius * Math.cos(phi); // Random sizes for stars sizes[i/3] = Math.random() * 2 + 1; } starGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); starGeometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1)); const starMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 2, sizeAttenuation: true }); const stars = new THREE.Points(starGeometry, starMaterial); scene.add(stars); assetLoaded(); // Count starfield as loaded } function createSun() { const sunGeometry = new THREE.SphereGeometry(planetaryData.sun.radius / SCALE_FACTOR * SUN_SCALE, 32, 32); const sunTexture = new THREE.TextureLoader().load( 'https://cdn.simulationtheory.ai/gasset/?asset=img&prompt=cartoon sun texture with orange and yellow swirls&w=512&h=512', function() { assetLoaded(); } // Callback when texture is loaded ); const sunMaterial = new THREE.MeshBasicMaterial({ map: sunTexture, color: 0xffdd00, emissive: 0xff8800, emissiveIntensity: 0.5 }); const sun = new THREE.Mesh(sunGeometry, sunMaterial); scene.add(sun); planets.sun = sun; // Add point light at sun's position const sunLight = new THREE.PointLight(0xffffff, 2, 5000); sun.add(sunLight); } function createPlanets() { // Create each planet for (const [key, data] of Object.entries(planetaryData)) { if (key === 'sun') continue; // Skip sun as it's already created // Create planet geometry and material const planetGeometry = new THREE.SphereGeometry( Math.max(data.radius / SCALE_FACTOR * SIZE_SCALE, 10), // Minimum size for visibility 24, 24 ); // Use a more cartoony material with some texture const planetTexture = new THREE.TextureLoader().load( `https://cdn.simulationtheory.ai/gasset/?asset=img&prompt=cartoon ${key} planet texture bright colorful&w=256&h=256`, function() { assetLoaded(); } // Callback when texture is loaded ); // Brighter, more saturated colors for a cartoony look const planetColor = new THREE.Color(data.color); planetColor.multiplyScalar(1.5); // Make colors brighter const planetMaterial = new THREE.MeshLambertMaterial({ map: planetTexture, color: planetColor }); const planet = new THREE.Mesh(planetGeometry, planetMaterial); // Add planet to scene scene.add(planet); planets[key] = planet; // Create orbit path with adjusted spacing createOrbitPath(data, key); // Create planet label createPlanetLabel(key, data.name); // Add Saturn's rings if applicable if (data.hasRings) { const ringGeometry = new THREE.RingGeometry( planet.geometry.parameters.radius * 1.3, planet.geometry.parameters.radius * 2.2, 64 ); const ringTexture = new THREE.TextureLoader().load( 'https://cdn.simulationtheory.ai/gasset/?asset=img&prompt=cartoon saturn rings texture colorful&w=512&h=512' ); const ringMaterial = new THREE.MeshBasicMaterial({ map: ringTexture, color: 0xf8e8c0, side: THREE.DoubleSide, transparent: true, opacity: 0.9 }); const ring = new THREE.Mesh(ringGeometry, ringMaterial); ring.rotation.x = Math.PI / 2; planet.add(ring); } } } function createOrbitPath(planetData, planetKey) { // Create elliptical orbit path with adjusted spacing const orbitPoints = []; const segments = 128; // Apply spacing adjustment for better visibility const adjustmentFactor = SPACING_ADJUSTMENT[planetKey] || 1; const adjustedSemiMajorAxis = (planetData.semiMajorAxis / SCALE_FACTOR) * adjustmentFactor; for (let i = 0; i <= segments; i++) { const theta = (i / segments) * Math.PI * 2; const semiMinorAxis = adjustedSemiMajorAxis * Math.sqrt(1 - Math.pow(planetData.eccentricity, 2)); // Calculate position on ellipse const x = adjustedSemiMajorAxis * Math.cos(theta); const y = 0; // Orbit plane const z = semiMinorAxis * Math.sin(theta); // Apply inclination const inclination = planetData.inclination * Math.PI / 180; const rotatedY = y * Math.cos(inclination) - z * Math.sin(inclination); const rotatedZ = y * Math.sin(inclination) + z * Math.cos(inclination); orbitPoints.push(new THREE.Vector3(x, rotatedY, rotatedZ)); } const orbitGeometry = new THREE.BufferGeometry().setFromPoints(orbitPoints); // More visible orbit paths with planet-specific colors const orbitColor = new THREE.Color(planetData.color); orbitColor.multiplyScalar(0.7); // Slightly darker than planet const orbitMaterial = new THREE.LineBasicMaterial({ color: orbitColor, transparent: true, opacity: 0.8, linewidth: 2 }); const orbit = new THREE.Line(orbitGeometry, orbitMaterial); orbit.visible = showOrbits; scene.add(orbit); orbits[planetKey] = orbit; } function createPlanetLabel(planetKey, planetName) { // Create div element for the label const labelDiv = document.createElement('div'); labelDiv.className = 'planet-label'; labelDiv.textContent = planetName; labelDiv.style.display = showLabels ? 'block' : 'none'; document.body.appendChild(labelDiv); planetLabels[planetKey] = labelDiv; } function updatePlanetPositions(days) { // Update each planet's position based on orbital parameters for (const [key, data] of Object.entries(planetaryData)) { if (key === 'sun') continue; // Skip sun const planet = planets[key]; if (!planet) continue; // Apply spacing adjustment for better visibility const adjustmentFactor = SPACING_ADJUSTMENT[key] || 1; const adjustedSemiMajorAxis = (data.semiMajorAxis / SCALE_FACTOR) * adjustmentFactor; // Calculate position in orbit const semiMinorAxis = adjustedSemiMajorAxis * Math.sqrt(1 - Math.pow(data.eccentricity, 2)); const meanAnomaly = (2 * Math.PI / data.orbitalPeriod) * days; // Solve Kepler's equation to get eccentric anomaly (E) let E = meanAnomaly; const e = data.eccentricity; // Newton-Raphson iteration to solve Kepler's equation for (let i = 0; i < 10; i++) { E = E - (E - e * Math.sin(E) - meanAnomaly) / (1 - e * Math.cos(E)); } // Calculate position in orbital plane const x = adjustedSemiMajorAxis * (Math.cos(E) - e); const z = semiMinorAxis * Math.sin(E); // Apply inclination const inclination = data.inclination * Math.PI / 180; const y = z * Math.sin(inclination); const rotatedZ = z * Math.cos(inclination); // Update planet position planet.position.set(x, y, rotatedZ); // Add a small rotation to make planets spin planet.rotation.y += 0.005; // Update planet label position updateLabelPosition(key, planet); } } function updateLabelPosition(planetKey, planet) { const label = planetLabels[planetKey]; if (!label) return; // Convert 3D position to screen coordinates const position = planet.position.clone(); position.project(camera); // Convert to screen coordinates const x = (position.x * 0.5 + 0.5) * window.innerWidth; const y = (- position.y * 0.5 + 0.5) * window.innerHeight; // Update label position label.style.transform = `translate(-50%, -50%) translate(${x}px, ${y}px)`; // Hide label if planet is behind the camera if (position.z > 1) { label.style.display = 'none'; } else { label.style.display = showLabels ? 'block' : 'none'; } } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } function onMouseMove(event) { // Calculate mouse position in normalized device coordinates mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; // Update raycaster raycaster.setFromCamera(mouse, camera); // Get intersected objects const intersects = raycaster.intersectObjects(Object.values(planets)); // Reset previously hovered planet if (hoveredPlanet) { const planetKey = Object.keys(planets).find(key => planets[key] === hoveredPlanet); // Reset scale with smooth transition gsap.to(hoveredPlanet.scale, { x: 1, y: 1, z: 1, duration: 0.3 }); hoveredPlanet = null; document.getElementById('info-panel').style.display = 'none'; } // Handle new hover if (intersects.length > 0) { const object = intersects[0].object; const planetKey = Object.keys(planets).find(key => planets[key] === object); if (planetKey) { hoveredPlanet = object; // Scale up with smooth transition gsap.to(hoveredPlanet.scale, { x: 1.2, y: 1.2, z: 1.2, duration: 0.3 }); // Show info panel showPlanetInfo(planetKey); } } } function showPlanetInfo(planetKey) { const planetData = planetaryData[planetKey]; const infoPanel = document.getElementById('info-panel'); const planetName = document.getElementById('planet-name'); const planetDetails = document.getElementById('planet-details'); // Set planet name planetName.textContent = planetData.name; // Clear previous details planetDetails.innerHTML = ''; // Add planet details for (const [key, value] of Object.entries(planetData.info)) { const detailItem = document.createElement('p'); detailItem.innerHTML = `${key.charAt(0).toUpperCase() + key.slice(1)}: ${value}`; planetDetails.appendChild(detailItem); } // Show info panel infoPanel.style.display = 'block'; } function togglePlayPause() { isPlaying = !isPlaying; const button = document.getElementById('play-pause'); button.textContent = isPlaying ? 'Pause' : 'Play'; button.classList.toggle('active', isPlaying); if (isPlaying) { clock.start(); } else { clock.stop(); } } function updateSpeed() { const slider = document.getElementById('speed-slider'); timeSpeed = parseFloat(slider.value); document.getElementById('speed-value').textContent = `${timeSpeed}x`; } function goToDate() { const datePicker = document.getElementById('date-picker'); const selectedDate = new Date(datePicker.value); if (!isNaN(selectedDate.getTime())) { // Calculate days difference const diffTime = selectedDate.getTime() - startDate.getTime(); elapsedDays = diffTime / (1000 * 60 * 60 * 24); // Update current date currentDate = new Date(selectedDate); updateDateDisplay(); // Update planet positions updatePlanetPositions(elapsedDays); } } function resetDate() { // Reset to current date startDate = new Date(); currentDate = new Date(); elapsedDays = 0; // Update date picker const dateString = currentDate.toISOString().split('T')[0]; document.getElementById('date-picker').value = dateString; // Update display updateDateDisplay(); // Update planet positions updatePlanetPositions(elapsedDays); } function toggleOrbits() { showOrbits = !showOrbits; document.getElementById('toggle-orbits').classList.toggle('active', showOrbits); // Update orbit visibility for (const orbit of Object.values(orbits)) { orbit.visible = showOrbits; } } function toggleLabels() { showLabels = !showLabels; document.getElementById('toggle-labels').classList.toggle('active', showLabels); // Update label visibility for (const label of Object.values(planetLabels)) { label.style.display = showLabels ? 'block' : 'none'; } } function resetView() { // Reset camera position and controls to see all planets gsap.to(camera.position, { x: 0, y: 300, z: 500, duration: 1, onUpdate: function() { camera.lookAt(0, 0, 0); } }); controls.reset(); } function updateDateDisplay() { document.getElementById('current-date').textContent = currentDate.toLocaleDateString(); } function animate() { requestAnimationFrame(animate); if (isPlaying) { const delta = clock.getDelta(); const daysElapsed = delta * timeSpeed; // Update elapsed days and current date elapsedDays += daysElapsed; currentDate = new Date(startDate.getTime() + elapsedDays * 24 * 60 * 60 * 1000); // Update date display updateDateDisplay(); // Update planet positions updatePlanetPositions(elapsedDays); } // Update controls controls.update(); // Render scene renderer.render(scene, camera); } // Initialize the scene when the page loads window.addEventListener('load', init); // GSAP for animations (minimal implementation) const gsap = { to: function(target, options) { const duration = options.duration || 1; const startTime = Date.now(); const startValues = {}; const endValues = {}; // Store start values and calculate changes for (const key in options) { if (key !== 'duration' && key !== 'onUpdate') { startValues[key] = target[key]; endValues[key] = options[key]; } } function update() { const elapsed = (Date.now() - startTime) / 1000; const progress = Math.min(elapsed / duration, 1); // Update values for (const key in startValues) { target[key] = startValues[key] + (endValues[key] - startValues[key]) * progress; } // Call onUpdate if provided if (options.onUpdate) { options.onUpdate(); } // Continue animation if not complete if (progress < 1) { requestAnimationFrame(update); } } // Start animation update(); } };