For my personal website’s landing page, I wanted something that felt alive without being too heavy or distracting. I drew heavy inspiration from Andrei Fomin’s Effulgence RPG (Steam Link Here) please go check it out, it’s awesome.
The Globe
The globe is just a collection of points sampled by latitude/longitude. I kept it simple: position, elevation, and a couple of indices for stable sampling. To avoid too much visual noise I skip the extreme poles (roughly -80 to 80) so the letters don’t compress into unreadable stacks at the top.
points.push({
x: x * radius,
y: y * radius,
z: z * radius,
elevation,
latIndex,
lonIndex,
});
Continents and terain gen
The terrain is layered noise, but with a few extra rules to keep it readable:
- Multiple octaves with different offsets so the frequencies don’t line up
- Continent seeds with light clustering so landmasses can connect
- Ridged noise for mountains, masked so peaks appear in belts rather than everywhere
- A reshaping curve to control land-to-sea ratio and keep peaks sharp
The seed is randomized on every refresh, so the planet stays familiar in style but never identical!
This approach is heavily inspired by Red Blob Games’ excellent write-up on terrain from noise generation:
https://www.redblobgames.com/maps/terrain-from-noise/
Please check out Amit Patel’s Red Blob Games site, it’s a treasure trove for coding parts of systems related to games.
The SUN SUN SUN
The sun is a separate sphere with its own mask. It does two jobs:
- Occlusion: hides globe characters directly under it (I think this is a major contributor to how cool it looks)
- Tint + warmth: shifts nearby characters toward the sun color
const sunTint = clamp(glowSoft * 0.65 + coreHard * 1.2 + relief * coreHard * 0.8, 0, 1);
const warmedColor = sunTint > 0 ? mixColor(baseColor, palette.sun, sunTint) : baseColor;
this isn’t production code, so I feel like I can get away with some magic numbers
I used it to compute a tint strength from distance-to-sun and terrain relief, then blend the base biome color toward a warm palette to make the light feel like it’s affecting the surface.
Bringing it together with animation
Most of the magic is just a small animation loop which advances time, updates the sun, gently steers rotation (or snaps to a POI), then renders.
const tick = (time) => {
const delta = Math.min((time - state.lastTime) / 1000, 0.05);
state.lastTime = time;
if (!prefersReducedMotion) {
state.sunAngle += delta * 0.45;
state.sunSpin += delta * 0.8;
state.autoRotation.y += delta * 0.35;
state.autoRotation.x = Math.sin(time * 0.0004) * 0.25;
}
updateRotation(time, delta);
render();
if (!prefersReducedMotion) requestAnimationFrame(tick);
};
Final thoughts
The end result is a tiny world that feels alive without being too flashy or distracting. It’s been a fun exercise in balancing complexity and readability, and I’m excited to keep iterating on it!