trevato/orbit

Public: anyone can read this; the crew can change it.

A 3D solar-system explorer in WebGL2, written by hand with no libraries: the eight planets and the Moon on real orbital elements, an orbit camera (drag to look, wheel to zoom, pinch on a phone), a time scrubber from real-time to years per second, and a facts card when you click a body. 60 fps on a laptop, usable on a phone, one page, keyboard shortcuts documented on screen.

Running 1e53c32 https://orbit--forge-trevato-apps.open-platform.sh in production 21d 16h

README.md

orbit

A 3D solar-system explorer in WebGL2, written by hand: the eight planets and the Moon on real orbital elements, an orbit camera, a time scrubber from real time to years a second, and a facts card for every body. One page, no dependencies, no assets — every vertex, every surface and the whole sky are generated in code.

PORT=8080 FORGE_APP_DATA=./data bun run src/server.ts   # run it here
bun test                                                # the check every pull runs
FORGE_APP_DATA=./data bun run src/tty.ts                # the terminal interface

Using it

Drag to turn, wheel or pinch to zoom, click a body for its facts. On a keyboard: 0–8 for the Sun and the planets outward, 9 for the Moon, n/p for the next and previous body, space to pause, , and . to slow down and speed up, ←/→ to step a day (with shift, an hour), t for now, o l r for orbits, labels and true sizes, s to name the view you are looking at, ? for the list on screen, escape to close a card. The address bar keeps the body, the date and the camera, so a reload or a shared link lands where you left it, and a named view is saved server-side.

On a phone the body list wraps so all ten are on screen — nothing is hidden behind a sideways scroll — the time bar is two fixed rows at the bottom, and the facts card is a sheet in the space between them. Closed, the sheet takes no room; tapping any body opens it, with the saved views underneath.

What is real, and what is not

Real. Positions come from Standish's Keplerian fit to JPL's ephemerides (src/bodies.ts), advanced to the date you are looking at by solving Kepler's equation. Over 1800–2050 — the range the fit covers, and the range the scrubber allows — the planets land within about an arcminute; the test suite checks that against five published opposition dates. The Moon runs on mean geocentric elements with no perturbation terms, so it can be a few tenths of a degree out, and Earth and the Moon both swing around their barycentre, 4,670 km off Earth's centre. Radii, rotation periods, axial tilts, ring radii and everything on the facts cards are measured values.

Not real. Surfaces: there are no photographs here. Bands, continents, ice caps, craters and solar granulation come out of value noise in the fragment shader, so a planet looks like itself without looking like a map. Bodies are never drawn smaller than about three pixels across, or the map would be empty black; press r for honest sizes and watch them disappear. Moons other than ours, asteroids, comets and the tilt of the ring particles are absent.

How it is built

  • src/kepler.ts, src/ephemeris.ts, src/bodies.ts — the astronomy.
  • src/mat4.ts, src/camera.ts, src/geometry.ts, src/scene.ts,

src/timeline.ts — the maths, all pure and all tested in bun test.

  • src/client/ — WebGL2: shaders.ts, renderer.ts, the 2D label overlay,

pointer and pinch input, and the loop in main.ts.

  • src/server.ts renders the page (src/pages/explorer.ts) with every body,

every fact and every saved view already in it, and src/bundle.ts builds the client at boot into one content-hashed file cached for a year.

  • src/views.ts — saved views in SQLite under FORGE_APP_DATA.

The camera renders in coordinates relative to itself and writes a logarithmic depth, which is what lets a 1,700 km moon and a 30 au orbit share one scene in float32. When frames run long the renderer draws fewer pixels rather than drop frames; the frame time is in the top bar.

It also has a terminal, which draws the same ephemeris looking down on the ecliptic in characters and shares the saved views: the exact command is in FORGE_APP_SSH when the app runs. It opens src/tty.ts on a real pty over the same data.

The forge sets PORT, FORGE_APP_DATA, FORGE_APP_ENV (production or preview), FORGE_APP_URL and FORGE_APP_COMMIT when it starts the app. forge.json says how.

↑↓ navigate ↵ open esc close

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs