3 min read
Live view globe
The admin draws the last 24 hours of orders on a globe in two places:
| Where | Renderer | Why |
|---|---|---|
| Home → Live view card | cobe (MIT), patched with a detailed land map | Small card, already cached on Home; cobe’s ~32k-dot ceiling is fine at that size. |
Analytics → Live view (/analytics/live) | Our own WebGL globe, live-globe/webgl-globe-canvas.tsx | Full-page hero where merchants zoom in on India. Solid land instead of dots: any dot pattern shimmered (moiré) at full-page size. |
Both take the same props (GlobeCanvasProps in live-globe/types.ts) and share the motion
and input code (globe-controls.ts: ease onto India, auto-rotate, drag to spin and tilt with
inertia, wheel / trackpad / touch-pinch zoom, reduced motion), the palette
(globe-palette.ts, read from the --muted-foreground, --primary and --chart-2 tokens)
and the overlay (globe-overlay.tsx: one focusable button per marker, the tooltip, the
zoom buttons). Only the drawing differs.
Our WebGL globe
No three.js: about 6 KB minified for the engine (webgl/), 12 KB for the whole lazy chunk
including the shared controls and overlay.
- Solid land, coast and borders in one shader (
webgl/globe-renderer.ts, WebGL 1 +OES_standard_derivatives). One full-screen triangle shades the sphere and the halo. Each pixel is turned back into latitude/longitude and reads the map texture (mipmapped, so it stays clean at every size): red is land, drawn darker than the ocean; the coast is where red crosses 0.5, and dividing byfwidthturns that into an on-screen distance, so the coastline is about one pixel wide at any zoom. Green holds country borders blurred on purpose: the shader turns the blurred value back into a distance (sqrt(-2 ln g)) in screen pixels for the same constant width. Faint 15° grid lines use the same trick. The longitude seam (whereatanjumps from π to −π) would make the mipmap blur a line down the 180° meridian, so two u coordinates with seams half a turn apart are computed and the continuous one is used. Coast, borders and grid fade towards the rim. Zoom goes to 4× (cobe: 2.6×). - Markers and arcs are point sprites on top (arcs are cobe’s quadratic curve drawn as a
chain of dots). Orthographic projection; anything behind the disc is dropped in the vertex
shader.
rotation()/anglesFor()are the same maths the overlay uses to place the marker buttons, so tooltips sit exactly on their markers. - Lazy loading.
live-globe-hero.tsximportswebgl-globe-canvaswith a dynamicimport()two animation frames after mount, so the stats cards paint first and nothing of the globe is in the Home bundle. The map (apps/admin/public/globe/globe-2048.png, about 330 KB) is fetched when the globe starts; until it arrives the sphere, markers and arcs draw without land.
The maps
Live view: Natural Earth 1:10m admin-0 countries, India point of view (public domain;
“Made with Natural Earth”), rasterised by scripts/globe/make-globe-map.py to a 2048×1024
equirectangular RGB PNG: red = land, green = borders between countries (outlines near the sea
are masked out; coasts come from the red channel). The point-of-view dataset draws India’s
boundary as the Government of India does — all of Jammu & Kashmir and Ladakh (including
Aksai Chin) and Arunachal Pradesh. Never replace it with the default-view file. To rebuild,
see the script’s docstring.
Home card: Natural Earth 1:50m land, rasterised by scripts/globe/make-land-map.py to a
1-bit 2048×1024 PNG inlined in patches/cobe@2.0.1.patch (cobe draws land only, no borders).
Tests
webgl/globe-renderer.test.ts covers the rotation (the focus point faces the viewer; east is
right, north is up) and the arc and marker point buffers. The shader itself is not exercised
in unit tests; check /analytics/live after changing it.