8 Jul 2026
Building this site
A one-page landing that grew a life of its own. It started as a name, a line about what I do, and a background — and then wanted an entries index, a feed, and somewhere to write, which is how most one-pagers end.
The background
The background is a single custom GLSL fragment shader: Gaussian metaballs — or raymarched platonic solids, if you catch it in the right mood — quantized through ordered dithering, with a colour system that draws a fresh OKLCH palette on every load. No two visits look quite the same, and the near-black background and off-white text are dark and light shades of that visit's accent, so the whole page shares one temperature.
Some things I learned along the way:
- Perceptually uniform colour spaces make randomness safe. Equidistant lightness in OKLCH means no palette ever comes out accidentally muddy.
- The cheapest place to compute something is once per frame on the CPU, not once per pixel on the GPU. Blob positions are worked out in JavaScript and handed to the shader as uniforms.
- Dithering hides a multitude of sins. Hard raymarch edges read as intentional when everything is chunky pixels anyway.
On a phone the shader takes the full screen with the heading sitting in its gravity, and the text goes below it, because a hero and a paragraph fighting over 400 pixels wide is a fight nobody wins.
Writing on it
Entries are written in Keystatic, a CMS with no database behind it — posts are files in the repository, and publishing is a commit. That suits a site with five posts on it far better than a database would, and it means the writing is backed up wherever the code is.
The public side of that is a block editor at /keystatic with links, images and video embeds, a published date, and a draft flag that keeps unfinished posts off the live site entirely. Around it: an entries index grouped by year, an RSS feed, a sitemap, structured data, generated social-share images per post, and a 404 that runs the same shader.
Colophon
Animation. A single custom GLSL fragment shader: Gaussian metaballs with per-blob sphere normals under an orbiting Lambert light, quantized by ordered 8×8 Bayer dithering. It runs on Paper Shaders' ShaderMount, with the dithering and wander maths adapted from the MIT-licensed paper-design/shaders. Blob positions are computed on the CPU once per frame and fed to the GPU as uniforms.
Colour. Palettes are generated fresh for every visit in OKLCH via culori — random hues with a minimum circular separation, gamut-clamped to maximum chroma, and guarded by a minimum perceptual distance (ΔE in OKLab) between every pair. The near-black background and off-white text are dark and light shades of the palette's accent, so the whole page shares one temperature.
Type. Space Grotesk by Florian Karsten, self-hosted through next/font. Set with text-wrap: pretty, cap-height trimming, and automatic hyphenation.
Stack. Next.js (App Router) and React, styled with Tailwind CSS, deployed on Vercel.
Icon. The favicon is a hand-generated SVG: a Lambert-shaded sphere pushed through the same 8×8 Bayer matrix as the site, one <rect> per lit pixel.
About these projects
I'm not a developer. Everything written up here — this site, HotterColder, Tomorrow, the Tumblr reader, the beat-synced video tool — was built by talking to an AI coding agent, at length and with a lot of arguing about details.
That's worth saying plainly, because it changes what the posts are for. None of this is a claim to engineering skill. What I did have was a clear idea of what each thing should feel like to use, and enough stubbornness to keep rejecting versions that didn't. The stacks and the design decisions are written up because they're genuinely interesting, and because the decisions turned out to be the part that mattered most.