Skip to Content
📄 ESSRT — Read the paper
3D SimulationTechnical Details

3D Simulation Technical Guide

This guide explains the concepts behind the Interactive 3D Solar System Simulation: the geocentric frame it renders in, the input constants that drive it, and how it computes positions. It is written for developers, researchers, and anyone curious about the technical implementation; basic familiarity with JavaScript and trigonometry is helpful but not required.

This page establishes:

Code-level documentation lives upstream. The simulator’s own docs are the source of truth for its internals — scene-graph hierarchy, constants reference, the render loop, the equation of centre and the N-body engine. They are kept current with the code; this page is not. See the docs/ directory in the simulator repository.


The Geocentric Frame

The simulation places Earth at the center of the scene — a geocentric reference frame. The Sun orbits Earth (not the other way around), and all planets revolve around the Sun, which itself revolves around Earth. This is a natural choice because the model describes precession cycles as seen from Earth’s reference frame.

How it works in the 3D scene:

The Sun has speed: 2π — exactly one revolution per year around barycenterEarthAndSun. Since barycenterEarthAndSun is nested inside Earth’s precession hierarchy, the Sun inherits all of Earth’s precession rotations while orbiting at 1 AU.

For other planets, a yearly revolution sandwich converts heliocentric orbits into the geocentric frame:

PerihelionFromEarth (speed: +2π/yr, perihelion offset position) └── ... └── RealPerihelionAtSun (speed: −2π/yr, orbit size, inclination tilt) └── Planet (own orbital speed)

The +2π layer makes the planet’s orbit center revolve at the same rate as the Sun — keeping the planet centered on the Sun as seen from Earth. The −2π layer cancels this revolution for the orbit inside, so the planet orbits at its own heliocentric rate. The net effect: the planet orbits the Sun, which orbits Earth, producing the correct apparent geocentric motion (annual retrograde loops, elongations, and zodiac transit).

The two base units of the scene are 2π radians = one solar year and 100 units = 1 AU; every period and distance is expressed relative to those.


Start Date Alignment

The simulation’s start date is aligned to the June Solstice of 21 June 2000, 00:00 UTC.

At this moment, the Sun was at its highest declination (RA ~6h / ~18h), corresponding to maximum obliquity effect.

The June Solstice doesn’t occur at exactly the same time each year. The calendar uses 365.2425 days (accounting for leap years), which doesn’t perfectly match the actual solar year length. The simulation accounts for this offset.


Source Code & Data

  • Simulation code: Available on GitHub  (see the simulation source files)
  • Model documentation: The simulator’s own docs — architecture, constants, verification gates — are in the repository’s docs/
  • Configuration: All values in the simulation match the Formulas and the theory described in this documentation

Input Parameter Reference

The simulation is driven by a set of input constants. All other values (day lengths, year lengths, precession rates, orbital positions) are derived from these inputs.

Core Cycle Parameters

ParameterDescription
holisticyearLengthThe fitted timing anchor (335,317 years) — the unit of the scene’s correction bases and of the legacy device chains. The published precession periods are the model’s dynamical values, not divisions of this anchor (the former integer-divisor reading is retired).
perihelionalignmentYearThe year when the longitude of perihelion was exactly 90° (aligned with December solstice). According to J. Meeus’s formula, this was 1246 AD. This anchor date determines where we are in all precession cycles.
perihelionalignmentJDThe same alignment date expressed as a Julian Day number (2,176,153), used for precise date calculations.
temperatureGraphMostLikelyA value from 0 to 16 (in 0.5 steps) that positions us within the obliquity cycle. Value 14.5 means we are 14.5 perihelion-of-date beats past the correction bases’ phase origin. It fixes that origin: 14.5 beats of ~20,938 years counted back from the 1246 AD alignment — a fitted phase, not a date.

Year & Day Length Parameters

ParameterDescription
inputmeanlengthsolaryearindaysThe reference solar year length (365.2422 days). This is the primary input from which mean values are calculated. The actual mean is rounded to fit exactly within the anchor interval (a device convention of the scene’s day-count bookkeeping).
meansiderealyearlengthinSecondsThe sidereal year in seconds (31,558,149.76). This is the J2000 anchor of the model — held constant within the modern-era scope, where all day/year calculations derive from it. The value itself drifts very slowly at geological timescales via solar mass loss (Kepler’s third law); see Expanding Resonance for the deep-time layer.
meansiderealyearAmplitudeinSecondsaDayControls how much the sidereal year (in days) varies over the perihelion precession cycle. The sidereal year in seconds is held at its J2000 value within the modern-era scope, but the number of days varies because day length changes with eccentricity.
meansolaryearAmplitudeinSecondsaDayControls how much the solar year varies over the obliquity cycle. As obliquity changes, the timing of equinoxes shifts slightly.
meanAnomalisticYearAmplitudeinSecondsaDayControls how much the anomalistic year (in days) varies over the perihelion precession cycle.

Model Start Position Parameters

ParameterDescription
startmodelJDThe model’s reference date as a Julian Day (2,451,716.5 = June 21, 2000 at 00:00 UTC). All orbital positions are calculated relative to this point.
startmodelYearThe same date as a decimal year (2000.5 = mid-2000). Used for year-based calculations.
whichSolsticeOrEquinoxDetermines which astronomical event the model aligns to at start: 0 = March Equinox, 1 = June Solstice, 2 = September Equinox, 3 = December Solstice. Default is 1 (June Solstice).
startAngleModelEarth’s orbital angle at the start date (89.91949879°). The June Solstice occurs at exactly 90°, but the model starts at midnight UTC which is slightly before the actual solstice time (01:47 UTC).
correctionDaysFine-tuning offset (-0.8288 days) to ensure the model aligns precisely with observed solstice times and produces clean Julian Day numbers at the bases’ phase origin t₀.
correctionSunAngular correction (0.49715°) because the model starts at 00:00 UTC but the June Solstice 2000 occurred at 01:47 UTC. This shifts the Sun’s starting position to match reality.

Physical Constants

ParameterDescription
currentAUDistanceThe length of 1 Astronomical Unit in kilometers (149,597,870.698828 km). Used for converting simulation units to real distances.
speedOfLightSpeed of light (299,792.458 km/s). Used for light-time calculations when computing apparent vs. true positions.
deltaTStartThe Delta-T value at the model start date (55.64 seconds). Delta-T is the difference between Terrestrial Time and UTC, which changes over time due to Earth’s variable rotation.

Right Ascension & Declination Calculations

All celestial position values in the simulation are calculated entirely in the browser - no external API calls. The calculations are deterministic based on the current Julian Date.

Step 1: Get World Positions

Each frame, the simulation retrieves the 3D positions of celestial bodies:

earth.rotationAxis.getWorldPosition(EARTH_POS); // Earth center sun.planetObj.getWorldPosition(SUN_POS); // Sun center obj.planetObj.getWorldPosition(PLANET_POS); // Current planet

Step 2: Transform to Earth-Equatorial Frame

The planet’s position is converted to Earth’s local coordinate system, which includes the axial tilt and the 90° rotation that starts the simulation at the June Solstice:

LOCAL.copy(PLANET_POS); earth.rotationAxis.worldToLocal(LOCAL); // World → Earth frame

The line earth.containerObj.rotation.y = (Math.PI/2) * whichSolsticeOrEquinox applies the 90° rotation so that 0h RA still points at the March Equinox even though the simulation begins at the June Solstice.

Step 3: Convert to Spherical Coordinates

SPHERICAL.setFromVector3(LOCAL); obj.ra = SPHERICAL.theta; // θ → Right Ascension (radians) obj.dec = SPHERICAL.phi; // φ → Declination (radians)

Since LOCAL is already in the tilted equatorial frame, θ directly gives the RA and φ gives the Declination - no additional trigonometry needed.

Step 4: Format for Display

The raw radians are converted to sexagesimal notation:

obj.raDisplay = radiansToRa(obj.ra); // "12h34m56s" obj.decDisplay = radiansToDec(obj.dec); // "+12°34′56″"

Earth’s own frame is placed from the engine each frame, so any change to its tilt, precession rate or date is reflected in the next projection.


How Other Planets Work

Every planet (Mercury through Neptune) uses the same counter-rotation sandwich as the geocentric frame conversion. The hierarchy, using Jupiter as an example:

barycenterEarthAndSun └── jupiterPerihelionDurationEcliptic1 (prograde, one turn per planets.jupiter.perihelionEclipticYears) └── PERIHELION JUPITER (speed: +2π/yr, perihelion offset) └── jupiterPerihelionDurationEcliptic2 (retrograde at the same rate, cancels #1) ├── Jupiter Real Perihelion At Sun (orbit size, inclination tilt, -2π/yr) │ └── Jupiter (orbital speed, axial tilt, mesh) └── Fixed Perihelion At Sun (fixed perihelion reference, sibling)

The same sandwich principle applies: Ecliptic1 carries the perihelion marker at the precession rate; Ecliptic2 cancels the precession for the orbit inside. The planet orbits the Sun in the correct heliocentric frame, while its perihelion direction precesses independently.

The +2π/yr and −2π/yr layers are the geocentric frame conversion — they make the orbit center revolve with the Sun while letting the planet orbit at its own rate inside.

Orbital inclination is encoded as a static tilt on the RealPerihelionAtSun container, decomposed into two components based on the J2000 ascending node (Ω) and inclination (i):

orbitTilta = i × sin(Ω) // rotation.x component orbitTiltb = i × cos(Ω) // rotation.z component

This orients the orbital plane at the correct angle to the ecliptic. The tilt is fixed at J2000 values — it does not precess in the 3D scene.

Dynamic inclination is computed analytically for the bodies with no element chain:

i(t) = mean + amplitude × cos(ω̃_ICRF(t) - cycleAnchor)

where ω̃_ICRF(t) is the ICRF perihelion longitude (ecliptic rate minus the device’s axial-line rate), and cycleAnchor is the per-planet ICRF perihelion longitude where MAX inclination occurs, evaluated at the bases’ phase origin t₀. Saturn’s cosine sign is flipped. Since the chain flip these law values are no longer what the panels display for the seven planets — the displayed inclinations (ecliptic and invariable-plane) come from the N-body element chain of date; the oscillation law remains for the no-chain bodies.

Key Differences from Earth

AspectEarthOther Planets
Orbital tiltTwo dynamic tilts creating obliquity + inclination cyclesStatic J2000 tilt on containerObj
InclinationGeometric (from two-tilt mechanism) + analytical formulaAnalytical formula only
Perihelion precession rateThe perihelion-of-date container (the published perihelion-of-date beat is ~20,938 years)The N-body chain’s own elements of date; the published periods are its leading secular eigenfrequencies

Moon

The Moon uses Earth as its center (not the Sun). It has six nested precession layers, following the same containerObj/orbitObj/pivotObj pattern. The signed layer rates sum exactly to the tropical month, and all layer phases are anchored to the J2000 lunar orbital elements:

earth.pivotObj └── moonApsidalPrecession — apsidal precession (+8.84765 yr of-date, prograde), carries eccentricity offset └── moonApsidalNodalPrecession1 — apsidal-nodal meeting cycle (−5.99701 yr, retrograde) └── moonApsidalNodalPrecession2 — counter-rotation (+5.99701 yr): the pair is inert (net zero) └── moonLunarLevelingCyclePrecession — apsidal canceller (−8.84765 yr, of-date pair): mirrors the apsidal layer in rate and phase └── moonNodalPrecession — nodal precession (−18.6132112 yr of-date, retrograde), pure rotation └── moon — draconitic-month orbital speed (27.2122209 d), carries the ecliptic inclination tilt

The apsidal precession layer carries the orbital eccentricity offset (orbitRadius). The moon container carries the ecliptic inclination tilt (orbitTilta/orbitTiltb) — placed below the nodal rotation so the nodal layer’s own spin regresses the tilted orbit plane (measured −19.35°/yr, matching the observed nodal plane motion). All intermediate layers are pure rotation layers with zero radius and zero tilt.

The detailed settings for each body are browsable in the simulation’s Planet Orbit Analysis (Tools menu) and in the published @essrt/model-values registry.


Summary

ComponentKey ValuePurpose
Base time unit2π = 1 solar yearAll periods relative to this
Base distance unit100 = 1 AUAll distances relative to this
Start dateJune Solstice 2000Reference point for all calculations
The fitted anchor335,317 yearsUnit of the correction bases (13.011 clock periods; not a cycle)
Phase origin t₀(a fitted phase, not a date)Phase origin of the correction bases — a chosen number of perihelion-of-date beats counted back from the 1246 AD mean-element alignment

The published periods are the model’s dynamical values (Earth’s Clock); the seven planets render from the N-body element chain.


Key Takeaways

  1. Two base units - 2π for time (1 year), 100 for distance (1 AU)
  2. Counter-rotating motions - Negative speeds (clockwise) vs positive speeds (counter-clockwise)
  3. Browser-based calculations - All RA/Dec values computed locally, no external APIs
  4. Geocentric frame — The Sun orbits Earth at 2π/year; planets use +2π/−2π layers for the heliocentric-to-geocentric conversion
  5. Cross-surface parity - All simulation values match the Orbital Calculator and the published model-values registry

Return to the Interactive 3D Simulation or explore the Formulas for the complete analytical formulas.

Last updated on: