Skip to contents

A tidy physics engine for building and visualizing orbital simulations in R.

New: the orbitr book. Orbital Mechanics with R — simulating planets, binary stars, chaotic systems, and the N-body problem with orbitr. Free to read online at book.orbit-r.com.

Version 1.0 — the function names, arguments, and defaults are now stable; anything that changes in a future version will be deprecated first. Feedback, bug reports, and contributions are welcome on GitHub.

orbitr is a lightweight N-body gravitational simulator built for the R ecosystem. Simulate planetary orbits, binary star systems, or chaotic three-body problems in a few lines of pipe-friendly code. Under the hood it ships a compiled C++ engine via Rcpp and falls back gracefully to a pure-R implementation.

Installation

# Install from CRAN:
install.packages("orbitr")

# Or install the development version from GitHub:
# install.packages("devtools")
devtools::install_github("DRosenman/orbitr")

Four Lines to an Orbit

For solar system bodies like the Sun and planets, you can use convenience functions like add_sun() and add_planet() — they use real masses and orbital data from JPL automatically:

library(orbitr)

create_system() |>
  add_sun() |>
  add_planet("Earth", parent = "Sun") |>
  add_planet("Mars",  parent = "Sun") |>
  simulate_system(time_step = seconds_per_day, duration = seconds_per_year * 2) |>
  plot_orbits()

Or build the whole solar system in one line with load_solar_system():

load_solar_system() |>
  simulate_system(time_step = seconds_per_day, duration = seconds_per_year) |>
  plot_orbits()

Don’t need every body? Use remove_body() to drop them:

load_solar_system() |>
  remove_body(c("Pluto", "Moon")) |>
  simulate_system(time_step = seconds_per_day, duration = seconds_per_year) |>
  plot_orbits()

You can also specify positions and velocities manually with add_body() — useful for custom or fictional systems, or when you want full control:

sim <- create_system() |>
  add_sun() |>
  add_body("Earth", mass = mass_earth, x = distance_earth_sun, vy = speed_earth) |>
  simulate_system(time_step = seconds_per_day, duration = seconds_per_year)

sim |> plot_orbits()
Closed elliptical trajectory of Earth orbiting the Sun over one year
Closed elliptical trajectory of Earth orbiting the Sun over one year

And animated:

animate_system(sim, fps = 15, duration = 5)
Animated GIF of Earth orbiting the Sun, with Earth leaving a fading trail as it moves
Animated GIF of Earth orbiting the Sun, with Earth leaving a fading trail as it moves

Features

  • Tidy output — simulate_system() returns a standard tibble (one row per body per time step), ready for dplyr, ggplot2, plotly, or anything else in the R ecosystem.
  • Built-in physical constants — real-world masses, distances, and orbital speeds for the Sun, all eight planets, and the Moon, so you don’t have to look anything up. See Physical Constants.
  • C++ engine — a compiled Rcpp acceleration kernel handles the O(n2)O(n^2) gravity loop, with automatic fallback to vectorized R if the compiled code isn’t available.
  • Three integrators — Velocity Verlet (default, symplectic, energy-conserving), Euler-Cromer (fast preview), and standard Euler (educational comparison). See The Physics.
  • 2D and 3D plotting — plot_orbits() returns a ggplot for planar sims and auto-dispatches to an interactive plotly widget when any body has Z-axis motion. See 3D Plotting.
  • Animations — animate_system() renders orbits as GIFs with fading trails via gganimate, or as interactive 3D animations with plotly.
  • Reference frame shifting — shift_reference_frame("Earth") re-centers the simulation on any body, turning a heliocentric view into a geocentric one. See Reference Frames.
  • Save and share — save_system() / load_system() round-trip a full system to an .rds file, and export_bodies() writes the body table to CSV for collaborators, Python, or Excel.

Kepler-16: A Real Circumbinary Planet

Kepler-16b orbits two stars — a real-life Tatooine. orbitr handles multi-body gravitational interactions natively, no special setup needed:

G  <- gravitational_constant
AU <- distance_earth_sun

m_A <- 0.68 * mass_sun
m_B <- 0.20 * mass_sun
a_bin <- 0.22 * AU

r_A <- a_bin * m_B / (m_A + m_B)
r_B <- a_bin * m_A / (m_A + m_B)
v_A <- sqrt(G * m_B^2 / ((m_A + m_B) * a_bin))
v_B <- sqrt(G * m_A^2 / ((m_A + m_B) * a_bin))

r_planet <- 0.7048 * AU
v_planet <- sqrt(G * (m_A + m_B) / r_planet)

create_system() |>
  add_body("Star A",      mass = m_A,                 x = r_A,      vy = v_A) |>
  add_body("Star B",      mass = m_B,                 x = -r_B,     vy = -v_B) |>
  add_body("Kepler-16b",  mass = 0.333 * mass_jupiter, x = r_planet, vy = v_planet) |>
  simulate_system(time_step = seconds_per_hour, duration = seconds_per_day * 228.8 * 3) |>
  animate_system(fps = 15, duration = 6)
Animated GIF of Kepler-16b orbiting a binary star system
Animated GIF of Kepler-16b orbiting a binary star system

Learn More