Globe

Interactive Earth — pause, pinch to zoom, and map surface points to coordinates.

Cursor Hover the globe
Selected Click to pin a coordinate
Zoom ×1.00
Status Spinning

Drag to rotate · Scroll or pinch to zoom (higher-res maps load in) · Pins stay screen-sized · At ×16 opens OpenStreetMap · Click to map lat/lon

Mapped points

  1. No pins yet — click the globe to add one.

Zoom wiki

How Earth Watch handles zoom, pins, map detail, and the hand-off to OpenStreetMap. Scroll or pinch on the globe — the HUD Zoom row shows the live level (fractional, two decimals).

1. Camera altitude → zoom level

Zoom is altitude-based, not a discrete step counter. At the default camera pose the level is ×1.00. As the camera dollies toward the surface, altitude shrinks and zoom rises continuously:

zoom = referenceAltitude / currentAltitude

Values are fractional (“sub-pixel”): ×1.00, ×1.37, ×2.85, ×8.40, up to ×16.00. Past the last day-map LOD the texture is simply enlarged — you see bigger pixels (digital / sub-pixel zoom) before any map hand-off. The OrbitControls min distance is set so the camera can reach exactly ×16 at the surface approach limit. Pinch and scroll zoom in altitude space (not raw camera distance), so sensitivity stays even from ×1 through ×16 instead of racing near the surface. Drag-to-rotate is scaled by 1 / zoom as well, so tiny grabs near ×16 no longer jump across the surface. The readout updates every animation frame.

2. Pins stay screen-sized

Pins are small 3D markers parented to the spinning Earth. Each frame their world scale is set proportional to camera altitude (distance above the surface), not origin distance:

pinScale = cameraAltitude / referenceAltitude

Closer camera → smaller world size → constant on-screen size. Pins do not grow when you zoom in; they keep a stable “HUD pin” footprint relative to the viewport.

3. Day-map LOD ladder

Closer views need sharper textures. Distance thresholds pick the highest available equirectangular day map; higher LODs load on demand and cache:

  1. 4K Blue Marble — default from far out (always available).
  2. 5.4K NASA BMNG — activates around distance ≤ 2.4; warmed in the background after first paint.
  3. 8K detail map — activates around distance ≤ 1.85; large source downscaled to the GPU max texture size (capped at 8192).

Status shows the active map name, or a loading label while a higher LOD fetches. Failed LODs are skipped and the next-best cached level stays on the globe. Beyond the top LOD the same texture keeps filling the view as zoom climbs — that is the intentional sub-pixel enlargement stage.

4. Hand-off at ×16 → OpenStreetMap

OpenStreetMap only kicks in after a long digital-zoom stretch. When zoom reaches ×16.00 (within a tiny epsilon) — about ×4 further than the earlier ×4 hand-off — the app pauses spin, disables orbit controls, and overlays an OpenStreetMap embed centered on the point under the camera. The OSM tile zoom is derived from the fractional globe zoom. Back to globe clears the iframe and eases the camera slightly under the threshold so the overlay does not immediately re-trigger. Reset view also exits the map overlay.

Pipeline each frame (summary)

  1. Apply spin (if enabled) and OrbitControls damping.
  2. Rescale every pin so on-screen size stays constant.
  3. Refresh the Zoom readout (×N.NN).
  4. Every ~120 ms: re-evaluate day-map LOD; if zoom ≥ ×16, switch to OpenStreetMap.
  5. Render the WebGL scene (skipped while the OSM overlay is active).

Click the globe (without dragging) to raycast the surface, convert the hit to lat/lon, and drop a screen-constant pin. Hover shows live cursor coordinates in the HUD.