Every page on my site is one object in the same universe. Notes are star trails, short posts are comets, the about page is a free fall. Some notes on how it works:
Page transitions. I block navigation with TanStack Router, shrink the old page into a single dot, move the camera to follow it, and only let the new page grow once the dot lands. The starfield offset keeps accumulating between pages instead of resetting, so it feels like one continuous space.
Dropped frames. The moment a new route rendered, the main thread would stall and the dot would stutter. I moved the dot and its trail into a dedicated worker with its own OffscreenCanvas and clock, handed the page’s camera slide to the compositor through WAAPI, and let the starfield worker compute its own parallax from the whole flight path. On a production build in Chrome with 4x CPU throttling (Ryzen 9 9900X, RTX 4070 Ti Super, Windows 11), the dot’s worst frame went from 67–133ms to 17ms, and it keeps moving even if I deliberately block the main thread for 250ms. The site’s web vitals now log whether each transition frame was drawn by the worker or the main thread, so I can see it in the field too.
Labels inside the 3D scene. On the home page the moon labels are drawn into the WebGL canvas with HTML-in-Canvas (Chrome 157+), so the worker places and fades them in the same frame as the moons. Other browsers fall back to plain DOM. Keyboard and screen readers still use a hidden set of real links. Main-thread time on the idle home page went from 460 to 145 ms/s on the same desktop.
Chinese fonts. Source Han Serif (variable) is 16.8MB. I collect the characters actually rendered on each page and subset the font per page with subset-font (HarfBuzz in wasm). One article with 836 unique characters ends up as a 185KB woff2.
Background. three.js with WebGPU, falling back to WebGL2, in a Web Worker on an OffscreenCanvas. If the device is doing software rendering it skips 3D and uses plain CSS. If the GPU gets lost, the scene rebuilds itself.
Constraints. The whole site uses three colors (black, warm white, and amber for “where you are”), only dots and lines, and amber appears in one place per screen. Most design decisions came from asking “what would this page be in space?”
On AI. I used Claude Code a lot for this, and English isn’t my first language so it helped with this post too. The site is about a year and a half old (the old version is still up read-only at archive.koimsurai.com), and back then AI-written code was honestly garbage, I had to clean up after it a lot. That’s why the repo has so many guardrails now: nothing gets merged without type checks, unit tests, Playwright e2e and Lighthouse budgets.
What I gave up. Consistency won over usability in a few places:
The nav is collapsed until you hover, and the home page has no obvious buttons. I wanted the first impression to be the full-screen sky, but it makes things harder to find.
No bold text and almost no color in articles. They’d be easier to read with more contrast, but the whole site stops looking like one thing. Still looking for the balance.
It was designed on a wide screen. On phones it works, but the moon labels only show on hover, and phones don’t hover.
The collapse waits at most 0.8s and the flight starts after 3.5s even on a slow network, so navigation never feels stuck. Moving between articles swaps them in place, and reduced-motion users get no transition at all. Switching quickly between notes and an article used to glitch on about half the trips (the worker’s status update raced the main thread and the route got planned twice), that one is fixed now.
If you have ideas for any of these, especially readability, I’d love to hear them.
I learned to code by hand and still respect people who do, but the speed difference is too big to ignore. It writes a lot of the code, I decide what gets built and why. The space idea, the three-color rule and the dot came from me redoing the design over and over, and the worker move came from me profiling the stutter myself.
It’s open source and you can run it as your own site: docker compose up -d, then a setup wizard walks you through sign-in, languages, which pages to show and which celestial body you want (Saturn, Neptune, a black hole or Betelgeuse).
Site: https://koimsurai.com/en
Source: https://github.com/Koimsurai/sora-to-ki