EPHEMERIS ← Return to the atlas

Field guide · Survey 04

How this atlas
was built

EPHEMERIS renders five different worlds with one sphere, one shader, and zero texture files. Everything you saw, the oceans, the lava, the rings, is mathematics evaluated live on your GPU. This page explains the whole machine, briefly, so you can build your own.

I

One planet, five climates

The obvious way to build this site is five planet models with five sets of textures. EPHEMERIS does something better: there is a single procedural planet, and each "world" is just a preset, a small bundle of about fourteen numbers and five colors fed into the shader. Scrolling does not swap anything in or out. It retunes the planet, the way turning a dial retunes a radio.

That is why the transitions feel alive rather than like page changes: between two sections you are watching an ocean world literally become a lava world, every value interpolating mid-flight.

UniformWhat it controls
uColA · uColB · uColCA three-stop color ramp the surface noise is mapped through
uNoiseScale · uWarpHow zoomed-in and how swirled the weather pattern is
uBandsBlends toward latitude stripes: 0 for rocky worlds, 1 for the gas giant
uDisplacePhysical bumpiness of the crust (vertex displacement)
uNightGlow · uGlowColorEmission on the dark side: lava rivers, plankton, auroras
uSpecSpecular glint, high for oceans and glass dunes
uAtmoColor + strengthThe fresnel rim and the halo shell around the planet
II

The surface: warped noise

The terrain pattern is fbm, fractal Brownian motion. Think of noise as smooth random static; fbm stacks several layers of it (called octaves), each one twice as detailed and half as strong. One layer looks like blobs. Five layers look like geography.

The signature move is domain warping: before sampling the noise, we bend its input coordinates using more noise. That bending is what makes Vela Thalassa's currents curl and Pyrrhos Cinder's lava run in filaments instead of sitting in polite round patches.

// bend space with noise, then sample noise in bent space
vec3 warp = vec3(fbm3(q), fbm3(q + 5.2), fbm3(q + 9.1));
float n = fbm(q + uWarp * warp);

// map the result through a three-stop color ramp
vec3 col = mix(uColA, uColB, smoothstep(0.08, 0.52, n));
col      = mix(col,   uColC, smoothstep(0.55, 0.95, n));

The gas giant adds one more trick: a sine wave across latitude creates cloud bands, and the same noise shears them so the stripes storm into each other instead of staying ruler-straight.

III

The light: day, night, and things that glow

Lighting is a single fixed "sun" direction with wrap lighting, a softened version of the usual dot product so the terminator (the day-night line) rolls off gently instead of cutting hard.

The emotional trick is on the night side. Where the surface noise is at its highest values, the dark hemisphere emits light: orange for Pyrrhos Cinder's lava script, teal for Thalassa's phosphorescent sea, pale green for the aurora light on Hiems Corona. A planet whose night side answers back reads as alive.

Two finishing touches: a specular glint (strong on the ocean world, absent on charred rock) and a fresnel rim, brightness that rises as the surface turns edge-on to you, which is what an atmosphere actually does.

IV

Atmosphere and rings

The halo is a second, slightly larger sphere rendered inside-out (side: BackSide) with additive blending, so it only shows as a soft glowing shell around the limb of the planet. Cost: almost nothing. Effect: the whole mood.

Noctua Pale's rings are a flat ring mesh whose density comes from one-dimensional value noise sampled along the radius, wide soft bands from low-frequency noise, fine grooves from high-frequency noise, multiplied together and faded at both edges. The rings' opacity is itself a preset value, so they simply are not there until the gas giant's section fades them in.

V

The scroll engine

The page is ordinary HTML sections, one per world. Each frame, the current scroll position is converted to a fractional world index, 2.4 means "40 percent of the way from Pyrrhos to Noctua", and every preset value is interpolated at that fraction.

Then, instead of applying that target directly, the live state chases it with exponential smoothing:

const chase = 1 - Math.exp(-dt * 4.2);
current[k] += (target[k] - current[k]) * chase;

That one line is why the planet feels like it has mass. Scroll fast and it leans into the change, then settles. It also makes the animation frame-rate independent, the same feel at 60 or 120 Hz.

VI

The design tokens

The visual identity is a star-atlas: parchment ink on a void, engraved gold rules, and monospaced survey data. Three typefaces, each with one job: Italiana for engraved display capitals, Instrument Sans for prose, IBM Plex Mono for anything a surveyor would have typed.

Void
#05070E
Firmament
#0B1224
Parchment
#E9E2CF
Astrolabe gold
#C7A55C
Thalassa
#5FD4C0
Pyrrhos
#E86A4E
Noctua
#9C8CE8
Sericata
#E0B36A
Hiems
#A6C8E8

Each world section carries its accent as a CSS custom property (style="--accent:#5FD4C0"), so the card chrome, the epithet, and the constellation nav all re-tint from a single attribute.

VII

Build your own

  1. Vendor everything. Fonts as local woff2 files, Three.js as a single module file. No CDNs means the template works offline and never breaks under someone else's outage.
  2. Start with the sphere and two colors. Get fbm mapped to a ramp on a lit sphere first. Every other feature is one uniform added to a working planet.
  3. Design worlds as presets, not scenes. Keep every world as a flat object of numbers. Tuning a world becomes editing nine lines, and blending comes free.
  4. Let scroll drive a fraction, not events. A fractional index plus exponential chase gives you inertia, interruptibility, and frame-rate independence in three lines of code.
  5. Respect the quiet paths. prefers-reduced-motion freezes rotation and parallax and lets presets snap. WebGL failure falls back to a gradient. Keyboard focus is visible throughout.
  6. Write the copy like it matters. The planet is the spectacle, but the survey notes are why you scroll to the end. Fictional worlds earn real feelings when the data grid plays it straight.