Projects-Portfolio

Solar System Positions · 1900–2100

An interactive 2D scatter-plot model of the solar system: the position of every planet, plotted for every month from January 1900 to December 2100, with a controllable timeline. 2412 monthly frames, 8 planets, one self-contained HTML file.

Solar system viewer preview

Problem

Textbook orbit diagrams are static snapshots. When you want to feel how the planets move — Mercury lapping Earth, Jupiter’s slow twelve-year circuit, Neptune barely creeping along — you need an animation over a long baseline. Most interactive solar-system demos fetch live data or cover a few years. This one shows the full 1900–2100 range with honest, reproducible ephemeris positions, and it lives entirely in the repository: no server, no CDN, no downloads at view time.

Approach

  1. Compute positions with Astropy. For the first day of every month, each planet’s barycentric ICRS position is differenced against the Sun’s and rotated into the mean ecliptic plane → heliocentric ecliptic (x, y) in AU, the classic top-down “map” of the solar system. The default ephemeris is Astropy’s built-in (ERFA) planetary model: fully offline, deterministic, and accurate to well beyond what a scatter plot needs. (An optional --ephemeris jpl --include-pluto flag produces arcsecond-grade JPL DE440s positions including the dwarf planet, at the cost of a one-time ephemeris download.)
  2. Generate the data: data/positions.csv — 19,296 rows (2,412 months × 8 planets), rounded to 1e-6 AU (~150 km), committed in full because it is small and reproducible.
  3. Build the viewer: viewer.html — a single self-contained file. A Canvas scatter plot with the Sun at the origin, planet dots on current-distance orbit guides, trails, a hover readout (exact AU from the Sun), and a timeline you can scrub with a slider, step with the arrow keys, or play back at 1–120 months/second. Log-scale toggle keeps the inner planets visible next to Neptune’s 30 AU. All 2,412 frames are embedded in the file — it works from GitHub Pages, any static host, or by double-clicking locally.

Results

Metric Value
Frames 2,412 (Jan 1900 → Dec 2100, monthly)
Bodies Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
Data rows 19,296
Data size ~700 KB CSV (committed)
Viewer size ~550 KB single HTML file
Ephemeris Astropy built-in (ERFA) — offline, deterministic
Precision 1e-6 AU (~150 km)
Tests hermetic: shape, finiteness, orbit ordering, golden Astropy checks

View the interactive chart: https://richard-ws.github.io/Projects-Portfolio/experiments/solar-system-positions/viewer.html (or open viewer.html locally — it is fully self-contained).

Validation highlights (all enforced by the test suite):

Quick start

python -m venv .venv
.venv/bin/pip install -e ".[dev]"

# regenerate the full dataset (offline, ~seconds):
.venv/bin/python scripts/generate_positions.py -c configs/example.yaml

# rebuild the viewer from the CSV:
.venv/bin/python scripts/build_viewer.py -c configs/example.yaml

# run the hermetic test suite:
.venv/bin/pytest

Optional JPL + Pluto variant (arcsecond-grade, needs one-time download):

.venv/bin/python scripts/generate_positions.py -c configs/example.yaml \
    --ephemeris jpl --include-pluto
.venv/bin/python scripts/build_viewer.py -c configs/example.yaml

Caveats

Layout

solar-system-positions/
├── configs/example.yaml      # range, bodies, ephemeris, output paths
├── src/solarpositions/
│   ├── config.py             # YAML config with numeric coercion
│   ├── positions.py          # Astropy ephemeris → ecliptic (x, y)
│   └── build.py              # CSV → embedded JSON → viewer.html
├── scripts/
│   ├── generate_positions.py # data/positions.csv
│   └── build_viewer.py       # viewer.html
├── templates/viewer_template.html  # the Canvas app (placeholder for data)
├── data/positions.csv        # committed generated data (19,296 rows)
├── viewer.html               # committed self-contained viewer
└── tests/                    # 20 hermetic tests