CSS-Driven Solar System

A Little Universe Built with Pure Code

This project started as “let me animate a planet real quick” and quietly escalated into a full solar system with moons, shadows, and a procedurally generated asteroid belt. All of it built with HTML + SCSS, zero JavaScript, and a dangerous amount of enthusiasm.

This is exactly the kind of thing I love doing in my spare time: using code to build tiny worlds, experimenting with motion, and seeing how far you can push CSS before it starts sweating.

See the Pen Solar System - Revised with Moons and Asteroids by Chad Danford (@cmd1209) on CodePen.

Fort the project if you like and send me a link of what you were able to come up with.

The simulation includes:
  • Planets orbiting in their correct relative positions
  • Moons (like the Moon and the four Galilean moons) following their planets
  • Dynamic shadows based on which side faces the sun
  • A fully generated asteroid belt that looks surprisingly “natural”
  • All orbits, sizes, and speeds adjustable through variables

Everything is parametric: change one value (like Earth’s orbital duration) and the whole system shifts in harmony. That’s the magic of SCSS loops, math, and a lot of trial and error.

The Hardest / Most Fun Part: The Asteroid Belt

CSS doesn’t give you real randomness, so creating 100+ asteroids that feel chaotic — different distances, different speeds, different starting positions — meant getting creative with SCSS’s pseudo-random numbers.

The “aha!” moment was realizing that you can fake natural distribution by randomizing:

  • orbital radius
  • animation speed
  • starting offset

The result? A belt that feels alive instead of looking like a dotted line someone forgot to align.

  /* Primary Palette */
//asteroids variables
$asteroid-number: 100
$asteroid-start: $au * 1.8
$asteroid-end: $au * 2.9

  @for $i from 1 through $asteroid-number 
  $randN: (random(10000) - 1) / 1000
  $asteroid-spread: random($asteroid-end - $asteroid-start) + $asteroid-start
  .orbit--asteroids:nth-of-type(#{$i})
    position: absolute
    top: 50%
    left: 50%
    border-radius: 50%
    transform-origin: center center
    width: #{$asteroid-spread * 2}
    height: #{$asteroid-spread * 2}
    margin-left: -#{$asteroid-spread}
    margin-top: -#{$asteroid-spread}
    animation: orbit (#{$ceres-year * (0.9 + random(20)/100)}) linear infinite
    animation-delay: -#{$randN * $earth-year}

.asteroid
  width: 2px
  height: 2px
  background-color: grey