Spaces:
Running
Running
File size: 48,871 Bytes
2337d23 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 | // Solar System Explorer - 3D Interactive Visualization with Moons
(function () {
'use strict';
// ============================
// PLANET & MOON DATA
// ============================
const PLANETS = [
{
name: 'Sun', radius: 696340, distance: 0, period: 0,
color: 0xFDB813, emissive: 0xFDB813,
rotationPeriod: '25.4 days (equator)',
description: 'The Sun is the star at the center of our Solar System, containing 99.86% of its total mass. It is a nearly perfect sphere of hot plasma generating energy via nuclear fusion.',
stats: {
'Type': 'G2V Main-sequence star',
'Diameter': '1,391,000 km',
'Mass': '1.989 × 10³⁰ kg',
'Surface Temp': '5,500°C',
'Core Temp': '~15 million°C',
'Rotation Period': '25.4 days (equator)',
'Age': '4.6 billion years',
'Composition': '73% Hydrogen, 25% Helium',
'Luminosity': '3.828 × 10²⁶ W'
},
moons: []
},
{
name: 'Mercury', radius: 2440, distance: 57.9, period: 0.24,
color: 0xb5b5b5, emissive: 0x222222,
rotationPeriod: '58.6 days',
description: 'Mercury is the smallest planet and closest to the Sun. Despite its proximity, it is not the hottest planet because it lacks a substantial atmosphere to trap heat.',
stats: {
'Distance from Sun': '57.9 million km',
'Orbital Period': '88 days',
'Rotation Period': '58.6 Earth days',
'Diameter': '4,879 km',
'Mass': '3.30 × 10²³ kg',
'Surface Gravity': '3.7 m/s²',
'Surface Temp': '-180°C to 430°C',
'Atmosphere': 'Virtually none',
'Moons': '0',
'Axial Tilt': '0.034°'
},
moons: []
},
{
name: 'Venus', radius: 6052, distance: 108.2, period: 0.62,
color: 0xe8cda0, emissive: 0x332200,
rotationPeriod: '243 days (retrograde)',
description: 'Venus is the hottest planet with surface temperatures of 462°C, caused by a runaway greenhouse effect. It rotates backwards and a day on Venus is longer than its year.',
stats: {
'Distance from Sun': '108.2 million km',
'Orbital Period': '225 days',
'Rotation Period': '243 days (retrograde)',
'Diameter': '12,104 km',
'Mass': '4.87 × 10²⁴ kg',
'Surface Gravity': '8.87 m/s²',
'Surface Temp': '462°C (average)',
'Atmosphere': '96.5% CO₂, 3.5% N₂',
'Pressure': '92× Earth\'s',
'Moons': '0',
'Axial Tilt': '177.4°'
},
moons: []
},
{
name: 'Earth', radius: 6371, distance: 149.6, period: 1.0,
color: 0x4b9cd3, emissive: 0x112244,
rotationPeriod: '23.93 hours',
description: 'Earth is the only known planet to harbor life, with a dynamic atmosphere, liquid water oceans covering 71% of its surface, and a magnetic field that shields it from solar wind.',
stats: {
'Distance from Sun': '149.6 million km',
'Orbital Period': '365.25 days',
'Rotation Period': '23 h 56 min',
'Diameter': '12,742 km',
'Mass': '5.97 × 10²⁴ kg',
'Surface Gravity': '9.81 m/s²',
'Surface Temp': '-89°C to 57°C',
'Atmosphere': '78% N₂, 21% O₂',
'Moons': '1',
'Axial Tilt': '23.44°',
'Magnetic Field': 'Yes (dipolar)'
},
moons: [
{ name: 'Moon', radius: 1737, distance: 0.384, period: 27.3, color: 0xaaaaaa,
description: 'The Moon is Earth\'s only natural satellite. It stabilizes Earth\'s axial tilt, causes ocean tides, and is the only celestial body beyond Earth visited by humans.',
stats: { 'Diameter': '3,474 km', 'Mass': '7.35 × 10²² kg', 'Orbital Period': '27.3 days', 'Rotation Period': '27.3 days (tidally locked)', 'Distance': '384,400 km', 'Surface Gravity': '1.62 m/s²', 'Escape Velocity': '2.38 km/s', 'Surface Temp': '-173°C to 127°C', 'Atmosphere': 'None (trace exosphere)', 'Composition': 'Silicate rock, iron core', 'Discovered': 'Known since antiquity', 'Notable': 'Tidally locked, human-visited' }
}
]
},
{
name: 'Mars', radius: 3390, distance: 227.9, period: 1.88,
color: 0xc1440e, emissive: 0x331100,
rotationPeriod: '24.6 hours',
description: 'Mars, the Red Planet, has the tallest volcano (Olympus Mons, 21.9 km) and the deepest canyon (Valles Marineris, 7 km deep) in the solar system. Evidence suggests it once had liquid water.',
stats: {
'Distance from Sun': '227.9 million km',
'Orbital Period': '687 days',
'Rotation Period': '24 h 37 min',
'Diameter': '6,779 km',
'Mass': '6.42 × 10²³ kg',
'Surface Gravity': '3.72 m/s²',
'Surface Temp': '-153°C to 20°C',
'Atmosphere': '95% CO₂, 2.7% N₂',
'Moons': '2',
'Axial Tilt': '25.19°'
},
moons: [
{ name: 'Phobos', radius: 11.3, distance: 0.009, period: 0.32, color: 0x8a7d6b,
description: 'Phobos is the larger of Mars\'s two moons. It orbits so close to Mars that it completes an orbit in just 7.6 hours — faster than Mars rotates. It will break apart in ~50 million years.',
stats: { 'Dimensions': '27 × 22 × 18 km', 'Mass': '1.07 × 10¹⁶ kg', 'Orbital Period': '7.66 hours', 'Rotation Period': '7.66 hours (tidally locked)', 'Distance': '9,376 km', 'Surface Gravity': '0.0057 m/s²', 'Escape Velocity': '11.39 m/s', 'Composition': 'Carbon-rich rock (C-type)', 'Surface Temp': '-40°C to -4°C', 'Discovered': '1877 by Asaph Hall', 'Notable': 'Stickney crater (9 km), spiraling inward' }
},
{ name: 'Deimos', radius: 6.2, distance: 0.023, period: 1.26, color: 0x9e9483,
description: 'Deimos is the smaller and outermost moon of Mars. Its smooth surface is covered in thick regolith that fills in most craters.',
stats: { 'Dimensions': '15 × 12 × 11 km', 'Mass': '1.48 × 10¹⁵ kg', 'Orbital Period': '30.3 hours', 'Rotation Period': '30.3 hours (tidally locked)', 'Distance': '23,460 km', 'Surface Gravity': '0.003 m/s²', 'Escape Velocity': '5.56 m/s', 'Composition': 'Carbon-rich rock (C-type)', 'Surface Temp': '-40°C', 'Discovered': '1877 by Asaph Hall', 'Notable': 'Slowly drifting away from Mars' }
}
]
},
{
name: 'Jupiter', radius: 69911, distance: 778.5, period: 11.86,
color: 0xc88b3a, emissive: 0x221100,
rotationPeriod: '9.93 hours',
description: 'Jupiter is the largest planet, with a mass 2.5× that of all other planets combined. It has the fastest rotation and a powerful magnetic field 20,000× stronger than Earth\'s.',
stats: {
'Distance from Sun': '778.5 million km',
'Orbital Period': '11.86 years',
'Rotation Period': '9 h 56 min',
'Diameter': '139,820 km',
'Mass': '1.90 × 10²⁷ kg',
'Surface Gravity': '24.79 m/s²',
'Cloud Temp': '-145°C',
'Atmosphere': '90% H₂, 10% He',
'Known Moons': '95',
'Great Red Spot': '~16,000 km wide',
'Axial Tilt': '3.13°'
},
moons: [
{ name: 'Io', radius: 1822, distance: 0.422, period: 1.77, color: 0xe5c94e,
description: 'Io is the most volcanically active body in the solar system, with over 400 active volcanoes driven by intense tidal heating from Jupiter\'s gravity.',
stats: { 'Diameter': '3,643 km', 'Mass': '8.93 × 10²² kg', 'Orbital Period': '1.77 days', 'Rotation Period': '1.77 days (tidally locked)', 'Distance': '421,700 km', 'Surface Gravity': '1.80 m/s²', 'Escape Velocity': '2.56 km/s', 'Volcanism': '400+ active volcanoes', 'Surface Temp': '-143°C (up to 1,700°C at vents)', 'Atmosphere': 'Thin SO₂', 'Discovered': '1610 by Galileo', 'Notable': 'Most geologically active body in solar system' }
},
{ name: 'Europa', radius: 1561, distance: 0.671, period: 3.55, color: 0xc8b89a,
description: 'Europa has a smooth icy surface concealing a global subsurface ocean that may contain 2-3× more water than all of Earth\'s oceans. A prime target in the search for extraterrestrial life.',
stats: { 'Diameter': '3,122 km', 'Mass': '4.80 × 10²² kg', 'Orbital Period': '3.55 days', 'Rotation Period': '3.55 days (tidally locked)', 'Distance': '671,034 km', 'Surface Gravity': '1.31 m/s²', 'Escape Velocity': '2.03 km/s', 'Ice Shell': '~15-25 km thick', 'Ocean Depth': '~60-150 km', 'Surface Temp': '-160°C', 'Discovered': '1610 by Galileo', 'Notable': 'Possible conditions for life' }
},
{ name: 'Ganymede', radius: 2634, distance: 1.07, period: 7.15, color: 0x8a8070,
description: 'Ganymede is the largest moon in the solar system — larger than Mercury. It is the only moon known to generate its own magnetic field.',
stats: { 'Diameter': '5,268 km', 'Mass': '1.48 × 10²³ kg', 'Orbital Period': '7.15 days', 'Rotation Period': '7.15 days (tidally locked)', 'Distance': '1.07 million km', 'Surface Gravity': '1.43 m/s²', 'Escape Velocity': '2.74 km/s', 'Magnetic Field': 'Yes (own magnetosphere)', 'Composition': 'Rock and ice (60/40)', 'Surface Temp': '-163°C', 'Discovered': '1610 by Galileo', 'Notable': 'Largest moon in solar system' }
},
{ name: 'Callisto', radius: 2410, distance: 1.88, period: 16.69, color: 0x6b6560,
description: 'Callisto is the most heavily cratered body in the solar system. Its surface is ~4 billion years old with no signs of geological activity.',
stats: { 'Diameter': '4,821 km', 'Mass': '1.08 × 10²³ kg', 'Orbital Period': '16.69 days', 'Rotation Period': '16.69 days (tidally locked)', 'Distance': '1.88 million km', 'Surface Gravity': '1.24 m/s²', 'Escape Velocity': '2.44 km/s', 'Surface Temp': '-139°C', 'Composition': 'Ice and rock (50/50)', 'Discovered': '1610 by Galileo', 'Notable': 'Most cratered body, possible subsurface ocean' }
}
]
},
{
name: 'Saturn', radius: 58232, distance: 1434, period: 29.46,
color: 0xe4d191, emissive: 0x222200, hasRings: true,
rotationPeriod: '10.7 hours',
description: 'Saturn is the least dense planet — it could float in water. Its ring system spans up to 282,000 km but is only about 10 meters thick on average.',
stats: {
'Distance from Sun': '1.43 billion km',
'Orbital Period': '29.46 years',
'Rotation Period': '10 h 42 min',
'Diameter': '116,460 km',
'Mass': '5.68 × 10²⁶ kg',
'Surface Gravity': '10.44 m/s²',
'Cloud Temp': '-178°C',
'Atmosphere': '96% H₂, 3% He',
'Known Moons': '146',
'Ring Span': '~282,000 km',
'Density': '0.687 g/cm³ (less than water)',
'Axial Tilt': '26.73°'
},
moons: [
{ name: 'Titan', radius: 2575, distance: 1.222, period: 15.95, color: 0xd4a54a,
description: 'Titan is the only moon with a dense atmosphere and the only body besides Earth with stable surface liquid (methane/ethane lakes and seas). Its thick orange haze hides a complex landscape.',
stats: { 'Diameter': '5,150 km', 'Mass': '1.35 × 10²³ kg', 'Orbital Period': '15.95 days', 'Rotation Period': '15.95 days (tidally locked)', 'Distance': '1.22 million km', 'Surface Gravity': '1.35 m/s²', 'Escape Velocity': '2.64 km/s', 'Atmosphere': '95% N₂, 5% CH₄ (1.5× Earth pressure)', 'Surface Temp': '-179°C', 'Surface Liquid': 'Methane/ethane lakes & seas', 'Discovered': '1655 by Huygens', 'Notable': 'Dense atmosphere, hydrocarbon cycle' }
},
{ name: 'Enceladus', radius: 252, distance: 0.238, period: 1.37, color: 0xffffff,
description: 'Enceladus has geysers shooting water ice from a global subsurface ocean through cracks at its south pole. It is considered one of the most likely places to find extraterrestrial life.',
stats: { 'Diameter': '504 km', 'Mass': '1.08 × 10²⁰ kg', 'Orbital Period': '1.37 days', 'Rotation Period': '1.37 days (tidally locked)', 'Distance': '238,000 km', 'Surface Gravity': '0.113 m/s²', 'Escape Velocity': '0.239 km/s', 'Surface Temp': '-198°C', 'Geysers': 'Water ice jets (south pole)', 'Ocean': 'Global subsurface (beneath ~30 km ice)', 'Discovered': '1789 by Herschel', 'Notable': 'Hydrothermal vents possible, astrobiology target' }
},
{ name: 'Mimas', radius: 198, distance: 0.186, period: 0.94, color: 0xbbbbbb,
description: 'Mimas has a giant impact crater (Herschel) spanning 1/3 of its diameter, making it resemble the Death Star. The impact nearly destroyed it.',
stats: { 'Diameter': '396 km', 'Mass': '3.75 × 10¹⁹ kg', 'Orbital Period': '22.6 hours', 'Rotation Period': '22.6 hours (tidally locked)', 'Distance': '185,520 km', 'Surface Gravity': '0.064 m/s²', 'Escape Velocity': '0.159 km/s', 'Surface Temp': '-209°C', 'Composition': 'Mostly water ice', 'Discovered': '1789 by Herschel', 'Notable': 'Herschel crater (130 km wide, 10 km deep)' }
},
{ name: 'Rhea', radius: 764, distance: 0.527, period: 4.52, color: 0xcccccc,
description: 'Rhea is Saturn\'s second-largest moon, a heavily cratered ice world. There is unconfirmed evidence it may have a thin ring system of its own.',
stats: { 'Diameter': '1,528 km', 'Mass': '2.31 × 10²¹ kg', 'Orbital Period': '4.52 days', 'Rotation Period': '4.52 days (tidally locked)', 'Distance': '527,000 km', 'Surface Gravity': '0.264 m/s²', 'Escape Velocity': '0.635 km/s', 'Surface Temp': '-174°C to -220°C', 'Composition': '75% water ice, 25% rock', 'Discovered': '1672 by Cassini', 'Notable': 'May have own thin ring system' }
},
{ name: 'Iapetus', radius: 735, distance: 3.56, period: 79.3, color: 0x665544,
description: 'Iapetus has a dramatic two-tone appearance — one hemisphere is as dark as coal, the other as bright as snow. It also has a massive equatorial ridge up to 20 km high.',
stats: { 'Diameter': '1,470 km', 'Mass': '1.81 × 10²¹ kg', 'Orbital Period': '79.3 days', 'Rotation Period': '79.3 days (tidally locked)', 'Distance': '3.56 million km', 'Surface Gravity': '0.223 m/s²', 'Escape Velocity': '0.573 km/s', 'Surface Temp': '-143°C to -173°C', 'Composition': 'Water ice and dark material', 'Discovered': '1671 by Cassini', 'Notable': 'Two-tone coloring, equatorial ridge (20 km high)' }
}
]
},
{
name: 'Uranus', radius: 25362, distance: 2871, period: 84.01,
color: 0x7de7e7, emissive: 0x113333,
rotationPeriod: '17.2 hours (retrograde)',
description: 'Uranus rotates on its side with an axial tilt of 98°, likely from a massive ancient collision. It is classified as an ice giant due to its composition of water, methane, and ammonia ices.',
stats: {
'Distance from Sun': '2.87 billion km',
'Orbital Period': '84 years',
'Rotation Period': '17 h 14 min (retrograde)',
'Diameter': '50,724 km',
'Mass': '8.68 × 10²⁵ kg',
'Surface Gravity': '8.87 m/s²',
'Cloud Temp': '-224°C',
'Atmosphere': '83% H₂, 15% He, 2% CH₄',
'Known Moons': '28',
'Axial Tilt': '97.77°',
'Rings': '13 known rings'
},
moons: [
{ name: 'Titania', radius: 789, distance: 0.436, period: 8.71, color: 0xbbaa99,
description: 'Titania is the largest moon of Uranus. Its surface shows a mix of impact craters and extensive fault systems (grabens) indicating past geological activity.',
stats: { 'Diameter': '1,578 km', 'Mass': '3.53 × 10²¹ kg', 'Orbital Period': '8.71 days', 'Rotation Period': '8.71 days (tidally locked)', 'Distance': '436,000 km', 'Surface Gravity': '0.379 m/s²', 'Escape Velocity': '0.773 km/s', 'Surface Temp': '-203°C', 'Composition': '50% ice, 30% rock, 20% organics', 'Discovered': '1787 by Herschel', 'Notable': 'Largest Uranian moon, canyons up to 1,500 km long' }
},
{ name: 'Oberon', radius: 761, distance: 0.584, period: 13.46, color: 0x998877,
description: 'Oberon is the outermost major moon of Uranus, heavily cratered with dark material on crater floors. A mountain ~11 km high was observed on its limb.',
stats: { 'Diameter': '1,523 km', 'Mass': '3.01 × 10²¹ kg', 'Orbital Period': '13.46 days', 'Rotation Period': '13.46 days (tidally locked)', 'Distance': '584,000 km', 'Surface Gravity': '0.346 m/s²', 'Escape Velocity': '0.727 km/s', 'Surface Temp': '-198°C', 'Composition': '50% ice, 30% rock, 20% organics', 'Discovered': '1787 by Herschel', 'Notable': 'Mountain ~11 km high, dark crater floors' }
},
{ name: 'Miranda', radius: 236, distance: 0.129, period: 1.41, color: 0xaaaaaa,
description: 'Miranda has the most extreme and varied terrain of any moon, including Verona Rupes — the tallest known cliff in the solar system at 20 km, where a dropped object would fall for 12 minutes.',
stats: { 'Diameter': '472 km', 'Mass': '6.59 × 10¹⁹ kg', 'Orbital Period': '1.41 days', 'Rotation Period': '1.41 days (tidally locked)', 'Distance': '129,390 km', 'Surface Gravity': '0.079 m/s²', 'Escape Velocity': '0.193 km/s', 'Surface Temp': '-187°C', 'Composition': 'Water ice and rock', 'Discovered': '1948 by Kuiper', 'Notable': 'Verona Rupes (20 km cliff), coronae features' }
},
{ name: 'Ariel', radius: 579, distance: 0.191, period: 2.52, color: 0xcccccc,
description: 'Ariel is the brightest and geologically youngest moon of Uranus, with extensive valleys and relatively few large craters suggesting resurfacing events.',
stats: { 'Diameter': '1,158 km', 'Mass': '1.35 × 10²¹ kg', 'Orbital Period': '2.52 days', 'Rotation Period': '2.52 days (tidally locked)', 'Distance': '190,900 km', 'Surface Gravity': '0.269 m/s²', 'Escape Velocity': '0.559 km/s', 'Surface Temp': '-213°C', 'Composition': '50% ice, 30% rock, 20% organics', 'Discovered': '1851 by Lassell', 'Notable': 'Brightest Uranian moon, youngest surface' }
}
]
},
{
name: 'Neptune', radius: 24622, distance: 4495, period: 164.8,
color: 0x4b70dd, emissive: 0x111133,
rotationPeriod: '16.1 hours',
description: 'Neptune has the strongest sustained winds of any planet (up to 2,100 km/h). It radiates 2.6× more energy than it receives from the Sun, suggesting an internal heat source.',
stats: {
'Distance from Sun': '4.5 billion km',
'Orbital Period': '164.8 years',
'Rotation Period': '16 h 6 min',
'Diameter': '49,244 km',
'Mass': '1.02 × 10²⁶ kg',
'Surface Gravity': '11.15 m/s²',
'Cloud Temp': '-214°C',
'Atmosphere': '80% H₂, 19% He, 1.5% CH₄',
'Known Moons': '16',
'Wind Speeds': 'Up to 2,100 km/h',
'Axial Tilt': '28.32°',
'Rings': '5 known rings'
},
moons: [
{ name: 'Triton', radius: 1353, distance: 0.355, period: 5.88, color: 0xcce0e8,
description: 'Triton orbits Neptune retrograde, strongly suggesting it was captured from the Kuiper Belt. It has active nitrogen geysers and is slowly spiraling inward toward Neptune.',
stats: { 'Diameter': '2,707 km', 'Mass': '2.14 × 10²² kg', 'Orbital Period': '5.88 days (retrograde)', 'Rotation Period': '5.88 days (tidally locked)', 'Distance': '354,759 km', 'Surface Gravity': '0.779 m/s²', 'Escape Velocity': '1.455 km/s', 'Surface Temp': '-235°C (coldest known surface)', 'Atmosphere': 'Thin N₂ (~0.00001 atm)', 'Geysers': 'Active nitrogen (8 km high)', 'Composition': 'Nitrogen ice, water ice, CO₂ ice', 'Discovered': '1846 by Lassell', 'Notable': 'Captured KBO, will break apart in ~3.6B years' }
},
{ name: 'Proteus', radius: 210, distance: 0.118, period: 1.12, color: 0x777777,
description: 'Proteus is one of the darkest objects in the solar system, reflecting only 6% of light. It is irregularly shaped and the largest of Neptune\'s inner moons.',
stats: { 'Diameter': '~420 km', 'Mass': '~5 × 10¹⁹ kg', 'Orbital Period': '1.12 days', 'Rotation Period': '1.12 days (tidally locked)', 'Distance': '117,647 km', 'Surface Gravity': '~0.07 m/s²', 'Shape': 'Irregular (box-like)', 'Albedo': '0.06 (extremely dark)', 'Discovered': '1989 by Voyager 2', 'Notable': 'Largest non-spherical moon in solar system' }
},
{ name: 'Nereid', radius: 170, distance: 5.51, period: 360.1, color: 0x999999,
description: 'Nereid has one of the most eccentric orbits of any known moon, ranging from 1.4M to 9.7M km from Neptune. It may be a captured asteroid or Kuiper Belt object.',
stats: { 'Diameter': '~340 km', 'Mass': '~3 × 10¹⁹ kg', 'Orbital Period': '360.1 days', 'Rotation Period': '~11.5 hours', 'Distance': '5.51 million km', 'Orbital Eccentricity': '0.7512 (very high)', 'Albedo': '0.155', 'Discovered': '1949 by Kuiper', 'Notable': 'Most eccentric orbit of any moon, possibly captured' }
}
]
}
];
// ============================
// SCALE FUNCTIONS
// ============================
function niceDistance(distMKm) {
if (distMKm === 0) return 0;
return 10 + Math.pow(distMKm, 0.45) * 2.5;
}
function niceSize(radiusKm, isSun) {
if (isSun) return 5;
return Math.max(0.4, Math.pow(radiusKm / 6371, 0.5) * 1.2);
}
function realDistance(distMKm) {
return distMKm / 10;
}
function realSize(radiusKm) {
// 1 unit = 100,000 km. Sun=6.96, Jupiter=0.70, Earth=0.064
const raw = radiusKm / 100000;
// Cap sun so it doesn't overwhelm at real-distance scale
if (radiusKm > 100000) return Math.min(raw, 3.0);
// Ensure tiny planets remain visible
return Math.max(raw, 0.03);
}
// ============================
// THREE.JS SETUP
// ============================
const scene = new THREE.Scene();
const cam = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 100000);
cam.position.set(0, 80, 120);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.toneMapping = THREE.ACESFilmicToneMapping;
document.body.insertBefore(renderer.domElement, document.getElementById('ui-overlay'));
const controls = new THREE.OrbitControls(cam, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.08;
controls.minDistance = 0.5;
controls.maxDistance = 50000;
controls.enablePan = true;
controls.zoomSpeed = 1.2;
controls.rotateSpeed = 0.6;
window.addEventListener('resize', () => {
cam.aspect = window.innerWidth / window.innerHeight;
cam.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// ============================
// STARFIELD
// ============================
(function createStarfield() {
const geo = new THREE.BufferGeometry();
const positions = [];
for (let i = 0; i < 8000; i++) {
const r = 5000 + Math.random() * 15000;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(2 * Math.random() - 1);
positions.push(r * Math.sin(phi) * Math.cos(theta), r * Math.sin(phi) * Math.sin(theta), r * Math.cos(phi));
}
geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
scene.add(new THREE.Points(geo, new THREE.PointsMaterial({ color: 0xffffff, size: 1.5, sizeAttenuation: true })));
})();
// ============================
// LIGHTING
// ============================
const ambientLight = new THREE.AmbientLight(0x333344, 0.4);
scene.add(ambientLight);
const sunLight = new THREE.PointLight(0xffffff, 2, 0, 1);
sunLight.position.set(0, 0, 0);
scene.add(sunLight);
// ============================
// CREATE PLANET MESHES
// ============================
const planetMeshes = [];
const orbitLines = [];
const labelDivs = [];
function createGlowTexture() {
const size = 256;
const cvs = document.createElement('canvas');
cvs.width = size; cvs.height = size;
const c = cvs.getContext('2d');
const g = c.createRadialGradient(size/2, size/2, 0, size/2, size/2, size/2);
g.addColorStop(0, 'rgba(255,200,50,1)');
g.addColorStop(0.2, 'rgba(255,150,30,0.6)');
g.addColorStop(0.5, 'rgba(255,100,10,0.2)');
g.addColorStop(1, 'rgba(255,50,0,0)');
c.fillStyle = g;
c.fillRect(0, 0, size, size);
return new THREE.CanvasTexture(cvs);
}
PLANETS.forEach((data, index) => {
const isSun = index === 0;
const radius = niceSize(data.radius, isSun);
const segments = isSun ? 64 : 32;
const geo = new THREE.SphereGeometry(radius, segments, segments);
let mat;
if (isSun) {
mat = new THREE.MeshBasicMaterial({ color: data.color });
} else {
mat = new THREE.MeshStandardMaterial({
color: data.color, emissive: data.emissive,
emissiveIntensity: 0.15, roughness: 0.8, metalness: 0.1
});
}
const mesh = new THREE.Mesh(geo, mat);
mesh.userData = { index, data, type: 'planet' };
scene.add(mesh);
planetMeshes.push(mesh);
if (isSun) {
const spriteMat = new THREE.SpriteMaterial({
map: createGlowTexture(), color: 0xffaa33,
transparent: true, blending: THREE.AdditiveBlending, depthWrite: false
});
const sprite = new THREE.Sprite(spriteMat);
sprite.scale.set(radius * 6, radius * 6, 1);
mesh.add(sprite);
}
if (data.hasRings) {
const ringGeo = new THREE.RingGeometry(radius * 1.3, radius * 2.3, 64);
const ringMat = new THREE.MeshBasicMaterial({ color: 0xc8b070, side: THREE.DoubleSide, transparent: true, opacity: 0.6 });
const ring = new THREE.Mesh(ringGeo, ringMat);
ring.rotation.x = Math.PI / 2.2;
mesh.add(ring);
}
// Orbit line
if (data.distance > 0) {
const orbitR = niceDistance(data.distance);
const orbitGeo = new THREE.BufferGeometry();
const pts = [];
for (let j = 0; j <= 128; j++) {
const a = (j / 128) * Math.PI * 2;
pts.push(Math.cos(a) * orbitR, 0, Math.sin(a) * orbitR);
}
orbitGeo.setAttribute('position', new THREE.Float32BufferAttribute(pts, 3));
const orbitLine = new THREE.Line(orbitGeo, new THREE.LineBasicMaterial({ color: 0x4499ff, transparent: true, opacity: 0.4 }));
scene.add(orbitLine);
orbitLines.push(orbitLine);
} else {
orbitLines.push(null);
}
// Label
const label = document.createElement('div');
label.className = 'planet-label';
label.textContent = data.name;
document.getElementById('ui-overlay').appendChild(label);
labelDivs.push(label);
});
// ============================
// MOON SYSTEM (created on-demand when planet is selected)
// ============================
let activeMoons = []; // { mesh, orbitLine, label, data }
let activeMoonParentIdx = null;
function clearMoons() {
activeMoons.forEach(m => {
scene.remove(m.mesh);
if (m.orbitLine) scene.remove(m.orbitLine);
if (m.label && m.label.parentNode) m.label.parentNode.removeChild(m.label);
});
activeMoons = [];
activeMoonParentIdx = null;
}
function createMoonsForPlanet(planetIndex) {
clearMoons();
const planetData = PLANETS[planetIndex];
if (!planetData.moons || planetData.moons.length === 0) return;
activeMoonParentIdx = planetIndex;
const parentMesh = planetMeshes[planetIndex];
const parentRadius = getPlanetRadius(planetData, planetIndex === 0) * parentMesh.scale.x;
planetData.moons.forEach((moonData, mi) => {
let moonOrbitR, moonRadius;
if (useRealSize) {
// Real proportional size: moon radius relative to parent
moonRadius = realSize(moonData.radius);
// Ensure tiny moons are still visible
moonRadius = Math.max(moonRadius, 0.015);
} else {
// Nice view: exaggerated for visibility
moonRadius = Math.max(0.15, parentRadius * 0.12 * Math.pow(moonData.radius / Math.max(planetData.radius * 0.05, 1), 0.25));
// Cap moon so it never exceeds 40% of planet size
moonRadius = Math.min(moonRadius, parentRadius * 0.4);
}
// Orbit radius
if (useRealDistance && moonData.distance) {
// Real distance: moon distance in million km / 10
moonOrbitR = realDistance(moonData.distance);
// Ensure minimum visible separation
moonOrbitR = Math.max(moonOrbitR, parentRadius + moonRadius + 0.5);
} else {
moonOrbitR = parentRadius * (2.5 + mi * 1.8);
}
// Moon sphere
const geo = new THREE.SphereGeometry(moonRadius, 16, 16);
const mat = new THREE.MeshStandardMaterial({
color: moonData.color, roughness: 0.9, metalness: 0.05, emissive: 0x111111, emissiveIntensity: 0.1
});
const mesh = new THREE.Mesh(geo, mat);
mesh.userData = { type: 'moon', moonIndex: mi, planetIndex, data: moonData };
scene.add(mesh);
// Moon orbit line
const orbitGeo = new THREE.BufferGeometry();
const pts = [];
for (let j = 0; j <= 64; j++) {
const a = (j / 64) * Math.PI * 2;
pts.push(Math.cos(a) * moonOrbitR, 0, Math.sin(a) * moonOrbitR);
}
orbitGeo.setAttribute('position', new THREE.Float32BufferAttribute(pts, 3));
const orbitLine = new THREE.Line(orbitGeo, new THREE.LineBasicMaterial({ color: 0x4499ff, transparent: true, opacity: 0.5 }));
scene.add(orbitLine);
// Moon label
const label = document.createElement('div');
label.className = 'planet-label';
label.textContent = moonData.name;
label.style.fontSize = '10px';
label.style.color = 'rgba(255,255,255,0.7)';
document.getElementById('ui-overlay').appendChild(label);
activeMoons.push({ mesh, orbitLine, label, data: moonData, orbitRadius: moonOrbitR, moonIndex: mi });
});
}
function updateMoons() {
if (activeMoonParentIdx === null) return;
const parentMesh = planetMeshes[activeMoonParentIdx];
const parentPos = parentMesh.position;
activeMoons.forEach((moon, i) => {
const angle = (time / (moon.data.period * 8 + 5)) * Math.PI * 2;
// Small inclination per moon for visual variety (±5°)
const inclination = (i * 0.05 - 0.05);
moon.mesh.position.set(
parentPos.x + Math.cos(angle) * moon.orbitRadius,
parentPos.y + Math.sin(angle) * moon.orbitRadius * inclination,
parentPos.z + Math.sin(angle) * moon.orbitRadius
);
moon.mesh.rotation.y += 0.01 * timeSpeed;
// Update orbit line position (centered on parent)
if (moon.orbitLine) {
moon.orbitLine.position.copy(parentPos);
moon.orbitLine.rotation.x = inclination;
moon.orbitLine.visible = showOrbits;
}
});
}
function updateMoonLabels() {
const halfW = window.innerWidth / 2;
const halfH = window.innerHeight / 2;
activeMoons.forEach(moon => {
if (!showLabels) { moon.label.style.display = 'none'; return; }
const pos = moon.mesh.position.clone();
pos.y += 0.5;
pos.project(cam);
if (pos.z > 1) { moon.label.style.display = 'none'; return; }
moon.label.style.display = 'block';
moon.label.style.left = ((pos.x * halfW) + halfW) + 'px';
moon.label.style.top = (-(pos.y * halfH) + halfH) + 'px';
});
}
// ============================
// STATE
// ============================
let timeSpeed = 1;
let time = 0;
let useRealDistance = false;
let useRealSize = false;
let showOrbits = true;
let showLabels = true;
let selectedPlanet = null;
let animatingCamera = false;
let cameraTarget = new THREE.Vector3();
let cameraDestination = new THREE.Vector3();
// ============================
// UPDATE PLANET POSITIONS
// ============================
function getOrbitalRadius(data) {
if (data.distance === 0) return 0;
return useRealDistance ? realDistance(data.distance) : niceDistance(data.distance);
}
function getPlanetRadius(data, isSun) {
if (useRealSize) return realSize(data.radius);
return niceSize(data.radius, isSun);
}
function updatePlanets() {
PLANETS.forEach((data, i) => {
const mesh = planetMeshes[i];
const isSun = i === 0;
const orbitalR = getOrbitalRadius(data);
if (orbitalR > 0) {
const angle = (time / (data.period * 200)) * Math.PI * 2;
mesh.position.x = Math.cos(angle) * orbitalR;
mesh.position.z = Math.sin(angle) * orbitalR;
mesh.position.y = 0;
} else {
mesh.position.set(0, 0, 0);
}
const targetRadius = getPlanetRadius(data, isSun);
const baseNiceRadius = niceSize(data.radius, isSun);
mesh.scale.setScalar(targetRadius / baseNiceRadius);
mesh.rotation.y += 0.002 * timeSpeed;
const orbitLine = orbitLines[i];
if (orbitLine) {
const positions = orbitLine.geometry.attributes.position.array;
for (let j = 0; j <= 128; j++) {
const a = (j / 128) * Math.PI * 2;
positions[j * 3] = Math.cos(a) * orbitalR;
positions[j * 3 + 1] = 0;
positions[j * 3 + 2] = Math.sin(a) * orbitalR;
}
orbitLine.geometry.attributes.position.needsUpdate = true;
orbitLine.visible = showOrbits;
}
});
}
// ============================
// LABELS
// ============================
function updateLabels() {
const halfW = window.innerWidth / 2;
const halfH = window.innerHeight / 2;
PLANETS.forEach((data, i) => {
const label = labelDivs[i];
if (!showLabels) { label.style.display = 'none'; return; }
const mesh = planetMeshes[i];
const pos = mesh.position.clone();
pos.y += getPlanetRadius(data, i === 0) * mesh.scale.x + 0.8;
pos.project(cam);
if (pos.z > 1) { label.style.display = 'none'; return; }
label.style.display = 'block';
label.style.left = ((pos.x * halfW) + halfW) + 'px';
label.style.top = (-(pos.y * halfH) + halfH) + 'px';
});
}
// ============================
// CAMERA (follows selected planet continuously)
// ============================
let followOffset = new THREE.Vector3(); // offset from planet to camera
let followingMoon = null; // reference to moon object if following a moon
function flyToBody(position, radius) {
const offset = radius * 4 + 5;
cameraTarget.copy(position);
cameraDestination.set(
position.x + offset * 0.5,
position.y + offset * 0.6,
position.z + offset
);
followOffset.set(offset * 0.5, offset * 0.3, offset);
followingMoon = null;
animatingCamera = true;
}
function flyToMoon(moonObj) {
const pos = moonObj.mesh.position.clone();
const offset = moonObj.orbitRadius * 0.4 + 2;
cameraTarget.copy(pos);
cameraDestination.set(pos.x + offset * 0.4, pos.y + offset * 0.5, pos.z + offset);
followingMoon = moonObj;
animatingCamera = true;
}
function animateCamera() {
// Continuous following: keep the selected planet (or moon) centered
if (selectedPlanet !== null && !animatingCamera) {
let targetPos;
if (followingMoon) {
targetPos = followingMoon.mesh.position;
} else {
targetPos = planetMeshes[selectedPlanet].position;
}
// Move both controls.target and camera by the planet's movement delta
const delta = targetPos.clone().sub(controls.target);
controls.target.copy(targetPos);
cam.position.add(delta);
}
// Fly-to animation (initial transition)
if (animatingCamera) {
let currentTargetPos;
if (followingMoon) {
currentTargetPos = followingMoon.mesh.position.clone();
} else if (selectedPlanet !== null) {
currentTargetPos = planetMeshes[selectedPlanet].position.clone();
} else {
currentTargetPos = cameraTarget;
}
const currentDest = currentTargetPos.clone().add(followOffset);
cam.position.lerp(currentDest, 0.05);
controls.target.lerp(currentTargetPos, 0.05);
if (cam.position.distanceTo(currentDest) < 0.3) {
animatingCamera = false;
}
}
}
// ============================
// RAYCASTER
// ============================
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
let pointerDownPos = { x: 0, y: 0 };
renderer.domElement.addEventListener('pointerdown', (e) => {
pointerDownPos = { x: e.clientX, y: e.clientY };
});
renderer.domElement.addEventListener('pointerup', (e) => {
const dx = e.clientX - pointerDownPos.x;
const dy = e.clientY - pointerDownPos.y;
if (Math.abs(dx) > 5 || Math.abs(dy) > 5) return;
mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, cam);
// Check moons first
const moonMeshes = activeMoons.map(m => m.mesh);
const moonHits = raycaster.intersectObjects(moonMeshes);
if (moonHits.length > 0) {
const hit = moonHits[0].object;
const moonObj = activeMoons.find(m => m.mesh === hit);
if (moonObj) {
selectMoon(moonObj);
return;
}
}
// Then planets
const planetHits = raycaster.intersectObjects(planetMeshes);
if (planetHits.length > 0) {
const hit = planetHits[0].object;
selectPlanet(hit.userData.index);
}
});
// ============================
// SELECTION & INFO PANEL
// ============================
function selectPlanet(index) {
selectedPlanet = index;
const data = PLANETS[index];
// Auto-enable real scale toggles
if (!useRealDistance) {
useRealDistance = true;
document.getElementById('toggle-real-distance').checked = true;
}
if (!useRealSize) {
useRealSize = true;
document.getElementById('toggle-real-size').checked = true;
}
showInfo(data);
const mesh = planetMeshes[index];
const radius = getPlanetRadius(data, index === 0) * mesh.scale.x;
flyToBody(mesh.position, radius);
createMoonsForPlanet(index);
updateNavButtons();
}
function selectMoon(moonObj) {
showInfo(moonObj.data);
const offset = moonObj.orbitRadius * 0.4 + 2;
followOffset.set(offset * 0.4, offset * 0.5, offset);
flyToMoon(moonObj);
}
function showInfo(body) {
const panel = document.getElementById('info-panel');
document.getElementById('info-name').textContent = body.name;
let html = '';
if (body.stats) {
Object.entries(body.stats).forEach(([key, val]) => {
html += `<div class="stat"><span class="stat-label">${key}</span><span>${val}</span></div>`;
});
}
if (body.description) {
html += `<p class="description">${body.description}</p>`;
}
document.getElementById('info-body').innerHTML = html;
panel.classList.remove('hidden');
}
document.getElementById('close-info').addEventListener('click', () => {
document.getElementById('info-panel').classList.add('hidden');
selectedPlanet = null;
followingMoon = null;
clearMoons();
updateNavButtons();
});
document.getElementById('btn-reset').addEventListener('click', () => {
animatingCamera = false;
selectedPlanet = null;
followingMoon = null;
cam.position.set(0, 80, 120);
controls.target.set(0, 0, 0);
controls.update();
document.getElementById('info-panel').classList.add('hidden');
clearMoons();
updateNavButtons();
});
// Speed
const speedSlider = document.getElementById('speed-slider');
const speedValueEl = document.getElementById('speed-value');
const playPauseBtn = document.getElementById('btn-play-pause');
let isPaused = false;
let savedSpeed = 1;
speedSlider.addEventListener('input', () => {
timeSpeed = parseFloat(speedSlider.value);
speedValueEl.textContent = timeSpeed.toFixed(1) + 'x';
if (timeSpeed > 0) {
isPaused = false;
savedSpeed = timeSpeed;
playPauseBtn.textContent = '⏸';
}
});
playPauseBtn.addEventListener('click', () => {
if (isPaused) {
// Resume
isPaused = false;
timeSpeed = savedSpeed || 1;
speedSlider.value = timeSpeed;
speedValueEl.textContent = timeSpeed.toFixed(1) + 'x';
playPauseBtn.textContent = '⏸';
} else {
// Pause
isPaused = true;
savedSpeed = timeSpeed || 1;
timeSpeed = 0;
speedSlider.value = 0;
speedValueEl.textContent = '0.0x';
playPauseBtn.textContent = '▶';
}
});
// Toggles
document.getElementById('toggle-real-distance').addEventListener('change', (e) => {
useRealDistance = e.target.checked;
if (useRealDistance) { cam.position.set(0, 300, 500); controls.target.set(0, 0, 0); }
else { cam.position.set(0, 80, 120); controls.target.set(0, 0, 0); }
controls.update();
if (selectedPlanet !== null) createMoonsForPlanet(selectedPlanet);
});
document.getElementById('toggle-real-size').addEventListener('change', (e) => {
useRealSize = e.target.checked;
if (selectedPlanet !== null) createMoonsForPlanet(selectedPlanet);
});
document.getElementById('toggle-orbits').addEventListener('change', (e) => { showOrbits = e.target.checked; });
document.getElementById('toggle-labels').addEventListener('change', (e) => { showLabels = e.target.checked; });
// Full illumination toggle - removes shadows so planets are fully visible
document.getElementById('toggle-fulllight').addEventListener('change', (e) => {
if (e.target.checked) {
ambientLight.intensity = 2.0;
sunLight.intensity = 0.3;
} else {
ambientLight.intensity = 0.4;
sunLight.intensity = 2.0;
}
});
// Planet nav buttons
const navContainer = document.getElementById('planet-nav');
PLANETS.forEach((planet, i) => {
const btn = document.createElement('button');
btn.textContent = planet.name;
btn.dataset.index = i;
btn.addEventListener('click', () => selectPlanet(i));
navContainer.appendChild(btn);
});
function updateNavButtons() {
navContainer.querySelectorAll('button').forEach((btn, i) => {
btn.classList.toggle('active', i === selectedPlanet);
});
}
// Keyboard
document.addEventListener('keydown', (e) => {
switch (e.key) {
case 'Escape':
document.getElementById('info-panel').classList.add('hidden');
selectedPlanet = null;
followingMoon = null;
clearMoons();
updateNavButtons();
break;
case 'ArrowRight':
if (selectedPlanet === null) selectedPlanet = -1;
selectPlanet((selectedPlanet + 1) % PLANETS.length);
break;
case 'ArrowLeft':
if (selectedPlanet === null) selectedPlanet = 1;
selectPlanet((selectedPlanet - 1 + PLANETS.length) % PLANETS.length);
break;
}
});
// ============================
// ANIMATION LOOP
// ============================
function animate() {
requestAnimationFrame(animate);
time += timeSpeed * 0.1;
updatePlanets();
updateMoons();
animateCamera();
updateLabels();
updateMoonLabels();
controls.update();
renderer.render(scene, cam);
}
animate();
})();
|