Our site now has a chapter where you scroll a camera through low orbit, visiting the four places we operate from. It looks like it should weigh fifty megabytes. It ships in roughly 1.5, and it is built almost entirely from public-domain science data.
Real data, baked hard
- The planet is NASA Blue Marble imagery, resized and recompressed by a build-time generator. Never hand-edited: when we want a change, we change the generator and bake again.
- City lights are not a texture. A script samples a NASA night-lights raster into about 46,000 weighted points packed into a small binary buffer, rendered as glowing sprites. Crisp at any zoom, because they are geometry, not pixels.
- Close-ups use a two-stage patch trick: whole-earth imagery is about 10 km per pixel, hopeless at 200 km altitude, so the generator bakes high-resolution patches (down to 76 m per pixel, from Sentinel-2 cloudless data) only around the four cities the camera actually visits.
The patches blend in the fragment shader, feathered at the edges so no seam ever shows, and the procedural detail noise fades out underneath them so photography is not fighting synthesis:
float fe = smoothstep(edge, edge - feather, dist); // patch edge feather
vec3 col = mix(base, patchTexel, fe * uPatch);
col *= 1.0 - patchAmt * 0.75; // damp noise under imagery
One line of trigonometry fixed the camera
Scroll drives a great-circle flight between stops. The obvious way to aim the camera, normalize(next - pos), collapses to a zero vector exactly at the endpoints, which is why naive fly-over cameras snap on arrival. The fix is the analytic tangent of the spherical interpolation itself, stable everywhere including t = 0 and t = 1:
// bearing along slerp(a, b) at parameter t, W = angle between a and b
tangent(t) = normalize( -cos((1 - t) * W) * a + cos(t * W) * b )
Arrivals settle north-up, hold while the camera descends, then rotate toward the next destination and depart with a slight bank, all of it driven by signed angles between that tangent and local north. Lighting is deliberately cinematic: the sun rises as you arrive so the terrain reads, and sinks back to a dawn band for the night flight.
Honest performance
ema = ema * 0.9 + frameMs * 0.1; // frame-time moving average
if (ema > 24.0 && px > FLOOR) {
px -= 0.2; // shed resolution before jank
renderer.setPixelRatio(px);
}
A governor watches the running frame time and steps internal resolution down before a weak GPU starts dropping frames, with a floor so text and coastlines stay sharp. Assets load only when the chapter approaches, and the whole thing idles at zero cost when off-screen. Awards-bait visuals are allowed. Awards-bait jank is not.
Originally published on the INTFRAME engineering blog. The companion code lives at github.com/intframe/scroll-rig.
Top comments (0)