Skip to main content

Playground · research instrument

Live Earth

A rotating globe with real data underneath it — actual coastlines, an astronomically accurate day/night line, real current weather, and the ISS tracked from its real orbital elements. Drag to spin.

Independent research instrument — not claimed as MakerPortal shipped product code. Methods, equations, assumptions, and limitations are disclosed so you can inspect what the page does and does not establish.

Globe

The layer, rotation and render style ride in the URL.

Day / Night

The terminator and lighting are updated continuously from your clock using the Sun’s apparent position and Greenwich sidereal time. Times below read in MakerPortal’s office zone (San Francisco, PDT/PST) with UTC underneath, so a reading can be checked against either.

Live time · office
Subsolar point

What's real here, and what isn't

A globe looks simple until you ask where each pixel comes from. This one has four independent data pipelines — only one runs purely in your browser. Here is what is real, what is simplified, and where the math lives.

Coastlines

Real geometry, self-hosted

Natural Earth 110m public-domain polygons, converted to GeoJSON and served from /data/world-land-110m.json. No live fetch to a map tile server. Projection is d3.geoOrthographic with clipAngle 9090^\circ, scale tied to canvas radius, updated on drag. We intentionally draw at low precision 0.10.1^\circ to keep frame rate while staying recognizable. Toggle wireframe vs filled — same geometry, two strokes.

Day / Night

Real astronomy from your clock

No API. We compute subsolar latitude δ\delta and longitude λs\lambda_s from Julian Date JDJD. Steps: T=(JD2451545)/36525T = (JD - 2451545)/36525, mean longitude LL, mean anomaly MM, equation of center C(M)C(M), apparent longitude, obliquity ϵ\epsilon, right ascension α=atan2(cosϵsinλ,cosλ)\alpha = \text{atan2}(\cos\epsilon \sin\lambda, \cos\lambda), declination δ=arcsin(sinϵsinλ)\delta = \arcsin(\sin\epsilon \sin\lambda). Greenwich sidereal time from same JDJD gives subsolar longitude.

Terminator is the great circle sp=0s \cdot p = 0 where ss is sun vector, pp is surface point. We build orthonormal basis u,vsu, v \perp s and sweep p(t)=costu+sintvp(t)=\cos t \, u + \sin t \, v, t[0,2π)t \in [0,2\pi). Night shading is per-pixel sn<0s \cdot n < 0 where nn is surface normal reconstructed from canvas x,yx,y plus viewer depth 1x2y2\sqrt{1 - x^2 - y^2}. Dot product sign decides day vs night, alpha ramps with sn-|s\cdot n|. That is why terminator is not a hard line but soft with grazing illumination.

This is exact to 0.01\sim0.01^\circ for modern dates — more than enough to watch sunset sweep across continents as you drag.

Weather

Real observations, hourly-cached snapshot

Sixteen cities, real current conditions from Open-Meteo. After first paint from the committed seed, the browser swaps in /api/globe-data.json; the CDN caches it for one hour and may serve it stale for a day while revalidating. The server batches all coordinates into one request, so no upstream URL or credential policy lives in the client. That batch response is positional, so each row is checked against the coordinate the provider echoes back before it is published — a city can be dropped, but it can never be shown another city’s weather.

Temperature to color is piecewise linear interpolation through stops 15-15^\circ to 4040^\circ C: deep blue \to cyan \to green \to amber \to red. Dot placed by projecting (ϕ,λ)(\phi,\lambda) through same orthographic projection. Hidden behind globe if pz<0p_z < 0 after rotation — d3-geo path returns null for backside, so cities disappear naturally as Earth spins, same as satellites.

Limitation: snapshot in time, not forecast. If you open at night and see 3232^\circ C in Mumbai, that was current at last refresh, not predicted.

ISS Tracker

Real TLE, simplified propagator

The same cached route fetches the ISS TLE from CelesTrak. A weekly GitHub Action refreshes src/data/satellite.json only as an outage seed; it no longer drives a site rebuild every four hours. Line 1 contains epoch yyddd.ddd, and line 2 contains i,Ω,e,ω,M0,ni, \Omega, e, \omega, M_0, n.

We parse nn rad/s, get semi-major axis a=(GM/n2)1/3a = (GM / n^2)^{1/3} from Kepler third law, GM=398600.4418GM=398600.4418 km3^3/s2^2. Mean anomaly at now M=M0+nΔtM = M_0 + n \Delta t, solve Kepler M=EesinEM = E - e \sin E by Newton iteration (15 iters), true anomaly ν=2atan2(1+esin(E/2),1ecos(E/2))\nu = 2 \text{atan2}(\sqrt{1+e}\sin(E/2), \sqrt{1-e}\cos(E/2)), radius r=a(1ecosE)r = a(1-e\cos E). Perifocal to inertial via R(Ω)R(i)R(ω)R(\Omega)R(i)R(\omega), then ECEF via GMST rotation θ\theta.

The propagator advances Ω,ω,M\Omega, \omega, M with their first-order J2J_2 secular rates before solving Kepler’s equation, then converts the result to WGS84 geodetic coordinates. The 4.95/day\sim-4.95^\circ/\text{day} nodal regression is the dominant term a two-body model gets wrong, and adding it is worth measuring rather than asserting: graded against a reference SGP4 implementation on the same elements, the ground-track error is 56 km at 24 hours where two-body gives 439 km, and 300 km after a week where two-body gives 3,060 km. What remains missing is real: drag BB^*, periodic terms, higher harmonics, resonance, and maneuver handling. Use satellite.js for pass prediction. The trail is recomputed from −25 minutes to now every second.

Honesty ledger

  • Coastlines — real shapes, fake shading. Orthographic is geometric idealization; no terrain, no atmosphere scattering.
  • Day/night — real sun position, simplified scattering. Night texture is a procedurally tinted alpha mask, not city lights database.
  • Weather — real temps at snapshot, not live, not forecast, 16 points only — spatial interpolation would be misleading so we show dots only.
  • ISS — real TLE, simplified physics, measured error. Graded against a reference SGP4 implementation on the same elements, the ground track here lands about 2 km out 4 hours after the TLE epoch, 56 km out at 24 hours, and 300 km out after a week. Position is WGS84 geodetic and altitude is within 5 km of SGP4. Illustrative, never a pass-prediction source.

Anatomy of the globe

Three independent data pipelines drive one canvas. Here is what each pixel is actually doing.

The render pipeline

  1. 01

    d3.geoOrthographic projection. The globe uses d3-geo's orthographic projection with clipAngle 90°, centered at (φ₀ = 18° tilt, λ₀ = variable spin). scale is tied to canvas radius. No WebGL — every line is drawn with Canvas 2D via d3.geoPath. Precision is 0.1° per d3 projection property, keeping tessellation light for smooth drag at 60 fps.

  2. 02

    Coastlines from Natural Earth. 110m-resolution public-domain polygons served from /data/world-land-110m.json, fetched once at init with an AbortController for cleanup. Drawn as geopaths — wireframe mode strokes them, filled mode fills them. Day/night mode uses 0.72 opacity; weather/ISS modes dim to 0.4 to keep data layers legible.

  3. 03

    Day/night painting. Every half-second, subsolar declination δ and longitude λ_s are computed from the Julian Date using the Astronomical Almanac formula. A 256×256 off-screen canvas pre-computes per-pixel sun-dot-product: for each visible disk pixel, the surface normal is reconstructed from screen (x,y) plus viewer depth, dotted with the sun vector. Negative dot = night, positive = day, with an alpha ramp for grazing illumination at the terminator edge. This is redrawn only when the solar minute changes or lambda0 shifts ≥ 0.2°.

Terminator great circle construction

Terminator={psp=0},p(t)=costu+sintv\text{Terminator} = \{ \mathbf{p} \mid \mathbf{s} \cdot \mathbf{p} = 0 \}, \quad \mathbf{p}(t) = \cos t \, \mathbf{u} + \sin t \, \mathbf{v}

Basis vectors u, v are orthonormal and perpendicular to sun direction s. The 180-point sweep traces the sunrise/sunset line across the globe. This line is what you see as the red curve — the exact boundary between illuminated and dark hemispheres.

Data overlays and interaction

  1. 01

    Weather mode. The committed seed paints immediately; one cached same-origin response replaces it after load. Its 16 Open-Meteo locations arrive in a single upstream request. Temperature maps through 5 color stops (−15°C deep blue → 40°C red), and d3-geo clips backside dots.

  2. 04

    ISS tracker. The cached same-origin response carries a real CelesTrak TLE. First-order J₂ rates advance Ω, ω, and M; 15 Newton iterations solve M = E − e sin E before the perifocal → ECEF and GMST rotations. The −25-minute trail is recomputed once per second.

  3. 05

    Auto-rotation and drag. The globe spins at 2.4°/s by default. Dragging pauses auto-rotation (lambda0 updates from pointer delta). An IntersectionObserver pauses the rAF loop when the canvas is off-screen to save CPU. ThemeObserver on data-theme triggers redraw for dark/light mode.

Gear behind this build

Earth & orbital mechanics stack · 8 picks

Orbital hardware8

More gear across every app: the full Gear list →

Two gotchas worth knowing

Orthographic depth clipping

d3.geoOrthographic with clipAngle 90° correctly omits geometry on the back half of the globe, but it does this per path segment — not per point. A long LineString that wraps around the back will be clipped into multiple visible arcs on the front. This is mathematically correct but can look like the ISS trail "jumps" when a portion of it moves behind the globe. The trail is re-sampled every second, so the jump smooths out.

Secular J₂ vs full SGP4

First-order J₂ removes the two-body propagator’s dominant plane-rotation error at very little code cost. It does not turn this into SGP4: atmospheric drag, short-period terms, higher gravity harmonics, third bodies, and maneuvers still accumulate error. “Illustrative, not precision tracking” remains the correct boundary.

Copyable: subsolar point & J₂ secular rates

The two compact equations at the center of the globe’s astronomy and orbit model.

JavaScript — subsolar point (declination, longitude)

function subsolarPoint(date) {
  const jd = date.getTime() / 86400000 + 2440587.5;
  const T = (jd - 2451545.0) / 36525;
  const meanLon = ((280.46646 + T * (36000.76983 + 0.0003032 * T)) % 360 + 360) % 360;
  const M = ((357.52911 + T * (35999.05029 - 0.0001537 * T)) % 360 + 360) % 360;
  const eqCtr = Math.sin(M) * (1.914602 - T * (0.004817 + 0.000014 * T));
  const appLon = meanLon + eqCtr - 0.00569;
  const obliquity = 23.439291 - 0.0130041667 * T;
  const ra = Math.atan2(Math.cos(obliquity) * Math.sin(appLon), Math.cos(appLon));
  const decl = Math.asin(Math.sin(obliquity) * Math.sin(appLon));
  const gmst = 280.46061837 + 360.98564736629 * (jd - 2451545.0);
  return { decl, lon: ra - gmst };
}

JavaScript — first-order J₂ secular rates

const J2 = 1.08262668e-3;
const RE = 6378.137; // km

function j2SecularRates({ a, e, i, n }) {
  const p = a * (1 - e * e);
  const cosI = Math.cos(i);
  const q = J2 * n * (RE / p) ** 2;
  return {
    raanDot: -1.5 * q * cosI,
    argpDot: 0.75 * q * (5 * cosI ** 2 - 1),
    meanAnomalyDot:
      n + 0.75 * q * Math.sqrt(1 - e * e) * (3 * cosI ** 2 - 1),
  };
}

Frequently asked questions

How accurate is the day/night terminator?

The terminator is computed from the Sun's apparent position using Julian Date, solar declination, and Greenwich sidereal time — the same astronomical formulas used in planetarium software. It is accurate to ~0.01° for modern dates, more than enough to watch sunset sweep across continents. The night shade is a per-pixel dot-product between the surface normal and sun vector, producing a soft transition rather than a hard line.

Why does the ISS trail sometimes clip at the horizon?

The ISS trail is a LineString projected through d3.geoOrthographic with clipAngle 90°. Points behind the globe (p_z < 0) are automatically omitted by d3-geo, so the trail appears to wrap around the visible hemisphere. This is the same mechanism that hides cities and coastlines on the back side of the Earth.

Is the ISS position accurate enough for pass prediction?

No, and the gap is measured rather than guessed. This simulation applies Keplerian motion plus first-order J₂ secular drift to real TLE elements, then reports WGS84 geodetic position. Graded against a reference SGP4 implementation on the same elements, its ground track is about 2 km out four hours after the element epoch, 56 km at 24 hours, and 300 km after a week; altitude stays within 5 km. Adding J₂ is what makes those numbers small — the two-body model it replaced is 439 km out at 24 hours and 3,060 km after a week — but drag, periodic J₂ terms, higher harmonics, maneuvers and lunar-solar perturbations are still absent. A visible pass lasts a few minutes, so tens of kilometres of along-track error is minutes of timing error. For precision tracking use satellite.js SGP4. This demo is for watching the ISS move, not for scheduling telescope passes.

How is weather data updated?

The browser fetches one same-origin endpoint cached at the CDN for an hour. That server route batches all 16 coordinates into one Open-Meteo request and fetches the ISS elements from CelesTrak, so the browser never calls either upstream directly. If an upstream is unavailable, the route serves the committed seed with its original timestamp instead of failing. The temperature-to-color mapping interpolates through five stops from −15°C (deep blue) to 40°C (red).

Why does the globe spin and can I control it?

The globe auto-rotates at 2.4°/second by default, giving a continuous view of all longitudes. You can pause rotation with the "Pause rotation" button, drag left/right to spin manually (horizontal drag maps to longitude delta × 0.3), and toggle between wireframe and filled rendering. The IntersectionObserver pauses the animation loop when the canvas is off-screen to save battery.

Shareable still

The instrument, captured—not illustrated.

This 16:9 frame is rendered from the real browser instrument above. It is the page's canonical preview for image search, link unfurls, and posts that need to show what the tool actually does.

Download 1280 × 720 JPEG
Live Earth — live MakerPortal instrument screenshot
Canonical capture · real UI · no generated scientific artwork