Learning laboratory

Predict, run, measure, explain.

Start by watching a world move. Then ask a question, make a prediction, and use an experiment to find out.

Your first orbit · about five minutes

  1. Meet the Earth–Moon family. Open Earth in the simulator. The view fits Earth and its Moon. Sizes and moon spacing are enlarged so you can see both.
  2. Take your time. Press Pause. Under Find an object, search for Moon and select it. Choose Show this system, then View selected object to close the picker.
  3. Make a prediction. Where will the Moon move next? Press Resume and watch. Change Simulation speed to see more or less time pass.
  4. Change your viewpoint. Open View options and switch trails from Absolute to Parent-relative. Close the panel. Notice how the Moon’s motion is easier to read relative to Earth.
  5. Explore again. Press Show whole system to return to the wide view. Restart returns to time zero; if you were paused, press Resume to watch again.

No keyboard or programming knowledge is needed. If the view keeps turning while physics is paused, turn off Rotate camera in View options. Need help? Find a control →

What would you like to discover?

  • Why does an orbit curve? Open Learn in the simulator and load the circular-orbit activity. In View options, enable vectors: green shows motion, orange shows acceleration. Their lengths are illustrative.
  • What happens if a world moves faster? Load Circular orbit, then expand Advanced physics settings in Learn. Change Starting speed multiplier from 1 to 1.1 and Load lesson again. Predict how its path changes.
  • Does a smaller calculation step help? The comparison lab is already configured for this question. Press Start comparison and inspect the phase-error measurements for A and B.

Loading a lesson returns it to its initial state and keeps your pause setting. Close Learn to watch, then Resume if paused. Changing playback speed only changes how quickly you watch; changing starting speed changes the physical experiment.

Lesson reference: predictions and measurements
LessonPredictMeasure and explain
Circular orbitAt one AU, v = sqrt(G M / r).Check constant radius, tangential velocity, and ε = −G M / (2r). Source: src/sim/lessons.c.
Eccentric orbitThe artificial orbit has a = 1 AU and e = 0.5; periapsis is 0.5 AU.Use v² = μ(2/r − 1/a). Speed is greatest near periapsis. Compare normalized energy change at smaller steps.
Escape thresholdv_escape = sqrt(2 G M / r); specific energy is near zero.Compare factors 0.99, 1 and 1.01. Explain bound, marginal and unbound trajectories. These lessons use Earth's mass/radius on deliberately artificial orbits.
Barycentric Earth–MoonBoth bodies move around their shared center of mass.Inspect total linear/angular momentum and toggle absolute/parent-relative trails. This isolated pair contains no Sun or third-body perturbations.
Inclined orbitA 30° rotation changes orientation, not orbital energy.Observe nonzero Y motion and compare radius/energy with the circular lesson. The common reference plane is X/Z.
Phobos resolutionSmaller steps improve phase even when all runs stay bound.Export 100 days at 300, 150 and 75 seconds; compare relative position. The normal runtime's 15-second step has a separate 100-day accuracy check.
Go deeper: why Verlet behaves differently

For timestep h, the integrator performs:

  1. v_half = v + a(x) h/2
  2. x_new = x + v_half h
  3. v_new = v_half + a(x_new) h/2

Explicit Euler instead advances position using the old velocity and velocity using the old acceleration. The learning test compares both against an independently calculated circular orbit. Halving Verlet's step reduces position error by approximately four at a fixed endpoint: second-order convergence.

make build/tests/test_learning_lab
build/tests/test_learning_lab

Energy change is normalized by K₀ + |U₀|, not total initial energy, so the escape lesson does not divide by a nearly zero quantity. Low total-energy error alone does not prove accurate satellite phase: large bodies dominate the total. Massless tracers contribute no energy or momentum. A fixed Sun constrains total linear momentum; use the free pair for a conservation experiment.

Read and plot downloaded measurements

Open CSV in a spreadsheet, skipping the leading # metadata lines. Filter one body, then plot normalized_energy_change against time_s for Euler and Verlet. Plot x_m against z_m with equal axis scales. For a moon, subtract its parent's coordinates at the same tick.

Record the lesson, speed factor, timestep, method, duration and revision alongside your conclusion. An initial-speed factor changes physics; playback speed changes how quickly fixed ticks are requested; illustrative magnification changes only drawing.

Orbital presets have point masses without tides or relativity; contact handling belongs only to the explicit two-sphere lesson. The exact-overlap gravity guard contributes zero acceleration and is a numerical convention, not a collision law. Close encounters can exceed a fixed step's useful resolution; a stable-looking picture is not sufficient proof.

Compare, save and share

The A/B comparison lab runs two C integrators from identical initial conditions. Choose a timestep or method difference, then compare matched checkpoints. Save a small .solar descriptor or create a link; opening it loads parameters without starting physics. Use the same source revision for a reproducible result; a local dirty revision does not uniquely identify uncommitted source changes.

build/solar-lab --compare examples/circular.solar > build/comparison.csv
build/solar-lab --compare examples/phobos.solar > build/phobos-comparison.csv
build/solar-lab --compare examples/collision.solar > build/contact-comparison.csv

Browser charts retain at most 1,025 uniformly coarsened points, including the current endpoint. The CLI streams every checkpoint. Physics step, measurement spacing and display-history resolution are separate choices: sparse straight segments are not a complete predicted orbit. A/B discrepancy is not itself ground truth; the analytical phase reference is available only for isolated two-body lessons.

Guided challenges ask you to meet a maximum sampled phase error below one degree over 100 simulated 24-hour days in the Phobos lesson, explain bound/unbound specific-energy signs, investigate the fixed-Sun momentum constraint, and prove that display changes leave exported SI data identical. Write a prediction before running.

Advanced model questions
  • Moving-Sun barycentric core: initial relative states stay the same while the mass-weighted position and velocity are subtracted from all bodies and the Sun is released. Compare momentum vectors, not only their magnitude; constant magnitude alone does not prove conservation.
  • Resonance: Jupiter perturbs a massless particle initialized at a 3:2 period ratio. Inspect 3λ_J − 2λ_particle − ϖ_particle for bounded libration versus circulation over long runs. Period commensurability is a hypothesis, not proof of resonance.
  • Close encounter: a probe begins four Earth radii sideways and forty radii upstream, with a controlled relative velocity. Compare coarse/fine deflection and minimum integrated distance. The method remains fixed-step.
  • Head-on contact: two chosen 10 kg, 10 m spheres approach at 10 m/s times the initial-speed factor. Steps are restricted to 0.01–0.25 s to prevent tunneling in this geometry. Elastic impulses conserve contact kinetic energy and momentum; merging conserves mass/linear momentum and combines volume while losing kinetic energy. Contact timing has finite-step error; there is no general collision or internal-spin model.

The force inspector exposes individual source acceleration vectors. For the Moon, solar acceleration can exceed Earth's contribution, yet Earth shares nearly the same solar acceleration. Subtract the parent's acceleration before interpreting relative binding. Percentages use the sum of magnitudes because source vectors may cancel.

Developer reference: run without graphics

A C11 compiler, Make and Python 3.10+ are enough for the headless laboratory. raylib is unnecessary for this path.

make headless
build/solar-lab --scene circular --days 30 --dt 300 --sample 3600 --output build/verlet.csv
build/solar-lab --scene circular --days 30 --dt 300 --sample 3600 --integrator euler --output build/euler.csv
build/solar-lab --help

Duration and sample spacing must be whole multiples of the timestep. Lessons accept steps from 0.01 to 3600 seconds and initial-speed factors from 0.1 to 2, with the tighter 0.01–0.25-second contact bound. These ranges permit intentionally inaccurate experiments; they are not promises of orbital accuracy. Invalid inputs fail before emitting CSV.

CSV includes revision, configuration, physical-data quality, simulation time/ticks, every body's absolute SI vectors, total energy, momentum and center of mass. Unknown physical values remain empty with an explicit unknown quality. Each sample streams directly to the output, so long runs do not retain an unbounded series in memory.

Web Advanced → Download measurements (CSV) and native E use the same C CSV writer. The browser downloads solar-snapshot.csv; native writes that name in the working directory. Compare matching lesson, parameters and ticks across native/WASM within floating-point tolerance, not different frame durations.

Developer reference: engineering exercises
  • C ownership: follow borrowed Body.name pointers, session-owned imported names, and trail allocation/destruction. Copying a struct containing an owned pointer does not duplicate its allocation.
  • Build graphs: edit a header and use make -n to see affected consumers. Run make test-build to verify dependency and exit-status contracts.
  • Profiling: run make build/benchmark_simulation and build/benchmark_simulation. Compare known-mass sources with massless tracers before changing the all-pairs solver.
  • Delivery: compile native and WASM from one revision, compare matching exported ticks, and inspect the checked artifact's provenance. make test-sanitize exercises memory and undefined-behavior boundaries.
  • Further research: use the existing Jupiter/moon scene and selected small-body experiments to study perturbations. Propose a measured bottleneck before trying a different data layout or approximate solver.