1  Why Simulate Gravity?

In 1609 Johannes Kepler published the first two of his three laws of planetary motion, and in 1687 Isaac Newton showed that all three follow from a single inverse-square force law (Kepler 1609; Newton 1687). For one planet going around one star, the problem is solved: write down the initial position and velocity, and the orbit is an ellipse whose size, shape, and orientation you can compute by hand, exactly, for all time. Chapter 4 does exactly that.

So why would anyone simulate it?

1.1 Three reasons

Because two is a special number. Add a third body and the exact solution disappears. Newton knew it; he spent years on the Moon’s motion under the combined pull of the Earth and the Sun and reportedly said it was the only problem that ever made his head ache. In 1890 Henri Poincaré proved that the three-body problem has no general solution of the kind Kepler’s ellipses provide, and in doing so discovered what we now call chaos (Poincaré 1890). Our own solar system has one star, eight planets, hundreds of moons, and a great many smaller things, every one of which pulls on every other. The planets follow Kepler’s ellipses almost, and the “almost” contains all of the interesting physics: Jupiter tugging Earth’s orbit, resonances in the asteroid belt, the slow precession of Mercury’s perihelion. None of it can be written down in closed form. All of it can be simulated.

Because the solution and the understanding are different things. Even for the two-body problem, knowing that the answer is an ellipse is not the same as knowing what happens when you give a planet 20% too much speed, or launch it at the wrong angle, or put it around a star of twice the mass. A simulation lets you ask those questions in seconds and see the answer. Most of the intuition that working astronomers have about orbits was built this way, by changing something and looking.

Because the simulation is the check on the theory. When you derive, on paper, that a body launched at \(\sqrt{2}\) times the circular speed should just barely escape, you can set up that exact situation and watch. If the simulation agrees, you understand both the physics and the code a little better. If it disagrees, one of them is wrong, and finding out which is where the learning is. Part II of this book is built around that loop: derive, simulate, compare.

1.2 What a simulation actually is

Here is the whole idea in four sentences. At any instant, every body has a position and a velocity. Newton’s law tells you the acceleration of every body from the positions of all the others. If you know where things are and how fast they are moving right now, you can estimate where they will be a short time \(\Delta t\) later. Do that, then do it again from the new state, and again, and you have an orbit.

The “estimate” is the whole subject of numerical integration, and it is where the craft is. The computer never solves the equations of motion. It takes small steps and hopes the error in each step is small enough, and cancels well enough, that the result after a million steps still means something. Whether that hope is justified depends entirely on how you take the step. Chapter 5 derives three ways of doing it, and shows that the most obvious one, the one every introductory text starts with, produces orbits that spiral outward and planets that quietly gain energy from nowhere. The one the package uses by default was invented for a different problem entirely and has a surprisingly deep reason for working.

This discreteness is the one thing to keep in mind every time you look at a simulation result. A simulated orbit is not the true orbit; it is the true orbit plus an error that depends on the step size and the method. Most of the time the error is invisible. Part of learning to simulate is learning when it is not.

1.3 What orbitr is

orbitr is an N-body gravitational simulator written for R (Rosenman 2026). You build a system by adding bodies to it with masses, positions, and velocities, run it forward in time, and get back a table. A few things make it different from the N-body codes that research astronomers use, and they are the reasons this book is possible:

  • The output is a tibble. One row per body per time step, with columns for time, position, and velocity. There is no special result object to learn. Every tool in the R data-analysis ecosystem (dplyr, ggplot2, plotly, anything that takes a data frame) works on it directly. Most of the analysis in this book is a filter(), a mutate(), and a ggplot().
  • It is built for the pipe. A simulation reads top to bottom: create a system, add bodies, simulate, plot.
  • Analysis is built in. The output can be re-centered on any body or on the system’s center of mass, reduced to orbital elements, checked against the conservation laws, and continued from where it stopped, each with one function.
  • The physics is visible. Three integration methods are available, so you can compare them. Softening is optional and off by default, so you get exact Newtonian gravity unless you ask otherwise. The gravitational constant is an argument, so you can turn gravity up, down, or off.
  • Real data is built in. Masses, orbital distances, and speeds for the Sun, the planets, and the Moon are included as constants, and the planets’ orbital elements come from the JPL DE440 ephemeris (Park et al. 2021), so add_planet("Mars", parent = "Sun") puts Mars where Mars actually is.
  • The inner loop is compiled. The pairwise force calculation runs in C++ through Rcpp (Eddelbuettel and François 2011), with a pure-R fallback, so systems of a few dozen bodies simulate in seconds.

1.4 What orbitr is not

It is not an ephemeris. If you need to know where Mars will be on a given date to arc-second precision, use JPL Horizons, which fits decades of radar and spacecraft tracking data and includes effects this package ignores. orbitr starts from JPL’s orbital elements at one epoch and integrates pure point-mass Newtonian gravity forward. Over a few years the planets land close to where they should; over centuries, small omitted effects accumulate.

The omissions are deliberate, and knowing them tells you what the package can and cannot answer:

  • Bodies are point masses. Gravity outside a spherically symmetric body is exactly that of a point mass at its center (Chapter 3 proves this), so for orbital distances this costs nothing. But there is no notion of two bodies touching, no oblateness, no tides.
  • Gravity is Newtonian. General relativity’s correction to Mercury’s perihelion precession, 43 arcseconds per century, is not in the model.
  • There are no non-gravitational forces: no atmospheric drag, no radiation pressure, no thrust.
  • The time step is fixed. Orbits that pass very close to a massive body need small steps near periapsis and would happily take large ones elsewhere; orbitr takes the same step throughout. Chapter 13 shows where this bites and how to handle it.

Chapter 16 returns to each of these, and to the package roadmap.

1.5 How to read this book

If you want to get something running first, go to Chapter 2 now, then come back. Part II can be read straight through as a physics text; the code is there to check the physics, not the other way round. Part III assumes Part II but each chapter stands alone, so you can go straight to binary stars or chaos if that is what you came for.

The most useful habit while reading is to predict before you run. Each chapter’s code sets up something whose outcome the physics tells you in advance; say what you expect, then look. When the two disagree, one of them is wrong, and finding out which is where the learning is.

1.6 Setting up

The preface lists what to install and how; the short version is R, orbitr 1.0.0 or later, and an editor that runs code chunks. Once that is done, compare your versions with the ones this book was built with:

packageVersion("orbitr")
#> [1] '1.0.0'
R.version.string
#> [1] "R version 4.6.1 (2026-06-24 ucrt)"

If packageVersion("orbitr") reports something older than 1.0.0, update: the analysis functions this book relies on were added in 1.0.0. Anything newer should run the book’s code unchanged; the changelog (NEWS.md in the GitHub repository, https://github.com/DRosenman/orbitr) lists any deprecations.