All projects
Cosmos Explorer
Open Source
web

Cosmos Explorer

Interactive 3D solar system and space-mission encyclopedia — Next.js 15 + Three.js front, Go + Connect-RPC backend, 281 prerendered bilingual pages and a live ISS tracker

By the numbers

0

Static pages (SSG+ISR)

0

Space missions

0

Protocols, one endpoint

0

Frontend tests

The Problem

What I was solving

3D space sites usually fall into one of two camps. Either a gorgeous WebGL demo with no real content, no SEO, and nothing for a search engine to index — or a content encyclopedia with static pages and zero interactivity. I wanted both at once: a real-time Three.js scene you can fly around AND hundreds of statically-rendered, indexable, bilingual pages — without maintaining two codebases or two APIs that drift apart.
My Approach

How I built it

Next.js 15 App Router in standalone mode. The 3D scene is a client island; the planet and mission pages are SSG + ISR — 281 prerendered at build, refreshed by cache-tags instead of a full rebuild. The Go backend exposes one Connect-RPC service that serves gRPC, gRPC-Web, and JSON from a single endpoint, so the browser, the Next.js server fetches, and any future gRPC consumer all hit the same contract. That contract is a Protobuf schema — buf generates both the TS client and the Go stubs from one .proto, so there are no hand-written DTOs to keep in sync. A WebSocket hub streams live ISS position at 1 Hz using SGP4 orbital math. Everything runs in Docker: distroless non-root backend, alpine non-root frontend. Deploy is GitHub Actions to GHCR to the VPS with a 120s health-gate, auto-rollback to the last good SHA, and ISR warm-up. The build trick: a throwaway Postgres + API comes up beside the build step so the frontend prerenders all 281 pages against real data, baked straight into the image.

Tech choices

  • Connect-RPC (one endpoint, three protocols)One service speaks gRPC, gRPC-Web, and plain JSON from a single endpoint. The browser uses a generated Connect client, Next.js server fetches hit the same contract, and there is no separate REST layer to maintain. gRPC-Web without a sidecar proxy is the part that sold me.
  • Protobuf + buf codegenThe .proto is the single source of truth. buf generates the TypeScript client and the Go stubs from it, so a field rename is one schema edit, not a hunt through hand-written DTOs on two sides. The contract literally cannot drift between front and back.
  • Next.js 15 SSG + ISR (281 pages)The 3D scene needs the client, but the encyclopedia needs to be indexable and fast. SSG prerenders all 281 pages at build; ISR refreshes them by cache-tag when content changes, so a single mission edit does not trigger a full rebuild. The best of static and dynamic without choosing.
  • Three.js r171 + post-processingEffectComposer with bloom gives the scene depth without a heavy engine. Orbital mechanics are real math at an artistic scale — Earth orbits at a readable distance, not a true-scale dot. Disposing geometries and materials on cleanup keeps a long session from leaking GPU memory.
Outcome

What came out of it

Live at cosmos.lavier.tech. 8 planets with orbital mechanics and bloom, a 128-mission encyclopedia with filters and search, a live ISS tracker over WebSocket, and time controls for pausing and speeding up the sky. 281 bilingual static pages with JSON-LD, hreflang, a DB-driven sitemap, and per-entity OG images — fast and indexable, not a black-box canvas. PWA with offline mode. One Connect-RPC endpoint serving three protocols off a single Protobuf contract. 122 frontend tests and a race-tested Go backend, all green in CI on every PR. Fully automated deploy with health-gate and auto-rollback — push to main and it ships itself.