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

Build orbit #2

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

orbit is now a WebGL2 solar-system explorer, written by hand with no dependencies. Positions come from Standish's Keplerian fit to JPL's ephemerides in `src/bodies.ts`, advanced by a Newton solve of Kepler's equation in `src/kepler.ts`/`src/ephemeris.ts`; the tests check them against five published opposition dates (Mars 2025‑01‑16, Jupiter 2024‑12‑07, Saturn 2025‑09‑21, Uranus 2025‑11‑21, Neptune…

shipped production is serving it for issue #1 deploy 1e53c32

opened this

orbit is now a WebGL2 solar-system explorer, written by hand with no dependencies. Positions come from Standish's Keplerian fit to JPL's ephemerides in src/bodies.ts, advanced by a Newton solve of Kepler's equation in src/kepler.ts/src/ephemeris.ts; the tests check them against five published opposition dates (Mars 2025‑01‑16, Jupiter 2024‑12‑07, Saturn 2025‑09‑21, Uranus 2025‑11‑21, Neptune 2025‑09‑23), against the published sidereal periods, and against the Earth–Moon barycentre lean of 4,670 km. The renderer (src/client/) draws stars, ten bodies with procedural surfaces, two ring systems with a planet shadow, orbit ellipses that fade behind their planet, and a corona — in camera-relative coordinates with a logarithmic depth buffer, which is what lets a 1,700 km moon and a 30 au orbit share one float32 scene. Drag, wheel, pinch, click-to-pick and every shortcut are documented behind ? on the page.

Worth looking at in the preview: click Earth and keep zooming — the Moon's orbit resolves, then Earth itself at true size; press r to drop the pixel floor and watch the planets vanish into honest scale; run the scrubber to years a second and Mercury blurs while Neptune barely moves. The frame time is in the top bar, and when it runs long the renderer drops resolution instead of frames. Saved views (s, or the form in the panel) live in SQLite under FORGE_APP_DATA and are listed, visited and deleted from src/tty.ts, which draws the same ephemeris looking down on the ecliptic in characters.

What I did not do: no textures or photographs — surfaces are value noise, so they look like themselves without pretending to be maps; no moons but ours, no asteroids or comets; the Moon runs on mean elements with no perturbation terms, so its longitude can be a few tenths of a degree out, and the scrubber stops at 1800 and 2050 where the element fit does. The scaffold's notes table is left alone rather than dropped. One thing I could not verify from here: this sandbox has no GPU, so the GLSL is checked by reading, not by running — the preview is the first time the shaders actually compile.

Closes #1

commented

On a 390px-wide phone the facts panel and the time bar crowd each other, and the top nav needs a hidden horizontal scroll to reach Mars and beyond. Make the phone layout work: stack the panel under the canvas or make it a sheet, and give the nav a visible way to reach every body. Also confirm the colour-ID readback for picking (the GPU stall due to ReadPixels warning) runs only on a click, never per frame — say so in the pull.

commented

The phone layout is rebuilt below 720px and the picking claim is confirmed by reading the code, not by assumption.

Layout. The body list now wraps (flex-wrap: wrap, overflow: visible) so all ten bodies are on screen at 390px in two rows — nothing behind a hidden sideways scroll. The time bar is two deliberate rows (play + scrubber + Now, then date + toggles, forced by a flex break), and its height is reserved in --bar instead of guessed, including env(safe-area-inset-bottom). The facts card became a sheet anchored above that bar, full width, max-height: calc(100dvh - var(--bar) - var(--top)) with its own scroll — so the two can no longer crowd each other. Closed, the sheet is display: none rather than an empty strip; tapping any body opens it, with the saved views underneath it. One consequence worth knowing: on a phone the save form is only reachable with a body selected, which is also the moment you'd want to save. The help panel got the same sheet treatment.

Picking never touches the GPU. There is no colour-ID pass and no readPixels, readBuffer or getBufferSubData anywhere in src/ — I grepped, and a test now asserts the bundled client contains none of them. Picking is Overlay.pick in src/client/overlay.ts:88: it walks the ten body centres projected during the last frame's overlay draw and takes the nearest within its screen radius or 22 px, pure CPU arithmetic. It is called from exactly two places, both pointer handlers in src/client/main.ts:159 (mouse hover on pointermove) and :163 (tap on pointerup). The render loop never calls it, so there is no per-frame stall.

Also fixed while checking the phone flow: the body chips are <a href="?b=…"> for the no-JS path, but nothing prevented the default navigation, so every tap was a full page reload that threw away the date and camera. They now preventDefault() and fly the camera; the links still work with the script off.

Three new tests cover the narrow-screen rules and the absence of GPU readback; bun test is 55 passing. What I still cannot verify from here is the rendering itself — this sandbox has no GPU or headless browser, so the shaders have been read carefully but never compiled; the preview is the first place they run.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs