What it is
A ground-station console over a 3D Earth with about 650 real satellites: crewed stations (the ISS and China's Tiangong core module), science missions such as Hubble, weather satellites, the operational GPS constellation and an evenly spaced sample of 480 Starlink satellites. Positions are computed in the browser with SGP4 from CelesTrak's published element sets, for whatever time is on the clock. The Earth is lit by the Sun for that moment, so the terminator, night-side city lights and satellites passing into Earth's shadow are all where they would be. Pick any satellite to see its orbit, ground track, the circle of ground that can see it, and its altitude, speed, period, inclination, perigee and apogee; follow the ISS up close as a 3D model.
Engineering decisions
- Plain Three.js, no globe library; CesiumJS was rejected for its size. satellite.js 7.1 does the SGP4/SDP4 propagation (only its JavaScript path is loaded; its WebAssembly builds are separate dynamic chunks that are never requested).
- The scene uses the equatorial inertial frame with Earth radius 1 and y to the north pole. The Earth mesh is turned by Greenwich mean sidereal time each frame, so orbits are drawn as the real ellipses while ground tracks (children of the Earth mesh) stay on the ground.
- Sun direction comes from the Astronomical Almanac low-precision formulas (unit-tested against the solstices and the equinox). Satellites in Earth's cylindrical shadow are dimmed in the point shader.
- Earth textures are painted in canvases at load from Natural Earth coastlines, lakes and populated places (public domain): ocean with a turquoise shelf near coasts and sea ice at the poles, land coloured by climate band with noise, bare rock and a snow line, a height map (real mountain ranges and ice sheets as soft uplift under ridged noise) that the shader turns into bump lighting, and an aux texture that packs city lights, the ocean mask for sun glint, and soft clouds into three channels. Land colours and clouds are decorative, not land-cover or weather data; the starfield is decorative too.
- Data:
scripts/build-orbit-data.mjs snapshots the CelesTrak GP groups into public/lab/orbit/sats.json (compact rows, dated) and the Natural Earth layers into earth.json. At load the page asks CelesTrak for the four small groups (CORS is open) with a 3.5 s timeout and a two-hour localStorage cache, as CelesTrak asks; on any failure the snapshot is used and the badge says so with its date. Starlink always comes from the snapshot (the full list is several MB). Docked modules that share the station's orbit are dropped. - All satellites are one
Points draw call, propagated every frame (every other frame on the low tier). Orbit rings for the visible groups (not Starlink) are one LineSegments, rebuilt when the clock moves more than three hours. Picking is screen-space: nearest point within 16 px (26 px on touch) that the Earth does not hide. - The ISS model is built in code: truss with alpha joints, eight solar array wings that turn towards the Sun, radiators, the US and Russian modules, Columbus, Kibo and docked capsules. It is drawn about 1,150 times its real size so it can be seen, and the card says so. The follow camera rides in the station's local frame (along the velocity, radial up).
- Reduced motion: time holds at now until moved, the globe does not turn by itself, and camera changes jump. Off screen or in a hidden tab the loop stops; the clock is wall-clock based so it is right when it resumes. Lost WebGL contexts show the poster and re-upload textures on restore.
Performance
Three.js and satellite.js load only on this page. The low tier (phones, ?quality=low) paints 2048 px textures without clouds, renders at pixel ratio up to 1.25 without antialiasing, uses fewer stars and orbit samples.