Civilization Star Map
Designing an interactive constellation where each star is a node of human knowledge — and making it work on every screen.
The Metaphor
Most portfolio sites arrange content in grids. Cards, lists, categories — legible, functional, forgettable.
What if each topic was a star instead? Not placed on a grid, but orbiting through a shared sky — drifting in and out of view like a living constellation.
That’s the idea behind the Civilization Star Map: mapping knowledge the way ancient civilizations mapped the heavens. Each star is a node. Each orbit is a relationship. The whole system breathes.

The Bigger Vision
This homepage constellation is a simplified preview of something larger I’m building toward: a space inside a future VR Academy called the Civilization Star Map.
Picture a walkway through open wilderness — no walls, no UI chrome, just terrain underfoot and sky overhead. As you walk, the path itself traces the arc of human progress: tools, language, writing, machines, networks, intelligence. Above you, the sky holds the people and moments who made each step possible — not as static portraits, but as living points of light you can approach, pause beside, and listen to.
The goal isn’t a museum. It’s closer to a rite of passage — a way for whoever walks that path to feel, physically, how hard-won and vast civilization’s progress has been, and how much of it rests on people who kept going anyway.
What lives on this page today — orbiting stars, hover to read, click to land here — is the seedling version of that walkway. The constellation is real; the wilderness is still being built.
I’m gathering a short bio and contribution note for each figure a little at a time. Eventually, clicking a star should lead into a fuller 3D/VR rendering of the walk itself. For now, every star in the sky brings you back to this page.
From Dots to Orbits
The first version was scattered dots that faded in and out. Felt like a screensaver.
The breakthrough: physics. Real celestial bodies follow orbits. So each star got:
- An elliptical path with unique eccentricity
- An orbital inclination (tilted orbit plane)
- A unique angular velocity — some drift slowly, others sweep through
- Depth — closer stars glow brighter, distant ones fade
The sky went from “random dots” to “something with intent.”
When motion follows physical plausibility — even simplified — our brains stop seeing “animation” and start seeing “behavior.”
The 2.5D Layer
Flat orbits felt predictable. Adding a Z-axis changed everything.
Foreground stars glow, cast box-shadows, respond to hover. Background stars fade to near-invisible, creating parallax. And a subtle Lissajous wobble makes each path slightly irregular — like gravitational tugs from unseen bodies:
const y = rawZ * 20 * Math.sin(star.inclination)
+ Math.sin(theta * 1.3 + star.nodeAngle) * 12 // primary wobble
+ Math.cos(theta * 0.7 + star.nodeAngle * 2) * 5 // secondary wobble
Three lines of math. Remove them and the motion immediately feels sterile. Keep them and most users won’t consciously notice — but the sky feels organic.
Interaction: Stars You Can Touch
Hover Pause
Hover a foreground star and two things happen:
- The entire sky freezes — all motion stops
- A tooltip card appears with the star’s identity
The pause is deliberate. A moment of stillness in a constantly-moving scene. It says: this one matters.

The Pointer-Events Trick
The star map sits behind the hero text. Text must be readable. Stars must be hoverable. These conflict.
The fix is CSS layering:
.hero-content {
pointer-events: none; /* mouse passes through text */
}
.hero-buttons {
pointer-events: auto; /* buttons stay clickable */
}
Your cursor passes through the title and reaches the stars behind it. Only buttons intercept. Feels magical.
One Sky, Every Screen
The hardest part wasn’t the math — it was the phone.
On desktop, stars orbit wide with overflow-visible, extending beyond their column. On a 375px screen, that same behavior creates a horizontal scrollbar.
The solution: one component, responsive container — no conditional rendering, no separate mobile build.
<div class="
absolute inset-0 overflow-hidden
lg:relative lg:overflow-visible lg:min-h-[500px]
">

Tablet — atmospheric background

Mobile — clean, no scrollbar
Mobile: absolute background, overflow-hidden clips at container edge.
Desktop: grid column, overflow-visible lets stars roam free. Page-level overflow-x-hidden catches them at the viewport.
Same animation. Same component. CSS does the rest.
Performance: 60fps with 125 Elements
Setting left and top every frame forces the browser to recalculate layout — 125 elements × 60fps = 7,500 layout passes per second. That’s jank.
The fix: transform: translate(). Transforms skip layout entirely and run on the GPU.
// ❌ Layout reflow every frame
el.style.left = `calc(50% + ${x}%)`;
// ✅ GPU compositing only
el.style.transform = `translate(${tx}px, ${ty}px)`;
Container dimensions are cached via ResizeObserver — no DOM queries inside the animation loop.
Animate with transform. Measure with ResizeObserver. Never read layout properties inside requestAnimationFrame.
What’s Next
- Bios for every star — a contribution note for each of the 45 figures, added gradually
- The walkway itself — a full 3D/VR rendering of the Civilization Star Map inside the VR Academy
- Dynamic data — stars generated from content, not hardcoded
- Constellation lines — connections between related stars
The sky is literally not the limit.
Built with React, TypeScript, and an unreasonable amount of time watching dots move across a screen.