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