This site is a prototype too.
One typeface family, one colour tone per project, one signature: the contour lines on the home page. This page explains how it is built. The numbers are measured on every build.
The contour lines
- What they are
- The contours of a soft noise field, like the lines on a hiking map, drawn by a shader on a canvas behind the whole page. On the first screen the same lines trace the labyrinth below: its depth is read as terrain, and scrolling melts it into the landscape.
- How they move
- They drift slowly and scroll at a third of the page’s speed, so they sit behind the content. Under your pointer the field rises and the lines give way like a wave.
The map
- What it is
- A small, playable version of Klangraum, my map of music. It uses the app’s data for 19 genres and the app’s blend rule: every place belongs to the nearest genre, and close to a border the neighbour mixes in, until both stand at 50% on the line itself.
- What it leaves out
- Subgenres, zoom and the music. In the app, Google’s Lyria model generates the blend live; here the readout names what you would hear. Two simplifications: a border considers every genre instead of direct neighbours only, and the coast is a radius around each genre.
- Three ways to draw it
- Before the script runs, and without it, CSS prints the map: a tone map of 138 × 148 values, one per dot, is multiplied with a dot pattern and sharpened by contrast. With WebGL2 a fragment shader takes over and adds the listening circle and the wave. Without WebGL2, the script moves the CSS print instead.
- Input
- Drag with momentum, tap a genre to glide there, or focus the map and use the arrow keys. On a touch screen a vertical swipe still scrolls the page; a sideways drag moves the map. The portrait on the About page is printed the same way.
The labyrinth
The figure behind the headline on the home page, here on its own: a flight through a Menger sponge, a cube that keeps splitting into smaller tunnels, drawn as fine lines through a kaleidoscope. Drag to spin it, click or tap to choose the next way, or focus it and use the arrow keys.
Motion
| Element | Behaviour | Timing |
|---|---|---|
| Headline | Lines rise out of a mask, once per page | 1 s, 80 ms apart |
| Contour lines | Drift slowly, rise under the pointer, pull together when you scroll fast | continuous, 30 frames a second |
| Project row | The cover grows out of the rule and runs along it with the pointer | 0.2 s, then a spring |
| Chapter titles | One-word titles widen as they scroll into view | tied to the scroll position |
| Senzu | Drag turns to the next view, letting go snaps to it | a spring, with the speed of the hand |
| Map | A wave from the needle, then one glide into the next genre | 1.7 s and 2.6 s, then it rests |
| Images | Open from the bottom edge when they scroll into view | 1 s |
| Page change | Title and cover of the project you clicked travel into its page | 0.6 s |
| Controls | Links, buttons and labels answer; pressing gives way | 0.2 s and 0.14 s |
One curve and three durations (0.2 s, 0.6 s, 1 s), shared by the stylesheet and the scripts, plus 0.14 s for the press of a control. Things that follow the hand or get thrown (project row, Senzu, the table, the map) run on springs instead, so they can be interrupted at any point. Keyboard actions do not animate. With “reduce motion” switched on in your system there is no smooth scrolling, no entrance, no page morph and no momentum; videos wait for a click.
Measured
- 55.2KBJavaScript on the home page, gzipped
- 210KBHome page, first load
- 13Requests on first load
- 0Cookies and trackers
Measured automatically on every build (last: 2026-10-02). Videos load only when they scroll into view and are not counted.
Accessibility
- WCAG 2.2 AA is the target. On the dark ground the text colour reaches 12 : 1 and the accent 7 : 1 in every project tone, so both also work as small text.
- Everything is readable without JavaScript, including the map. If the script fails, hidden content appears after 1.2 seconds anyway.
- The map works with the keyboard: focus it, then use the arrow keys. A screen reader hears the blend once the map has come to rest.
- Every looping video has a pause button. Headlines are split into lines for the entrance only; assistive technology gets the whole sentence.
Your browser
- Page-to-page view transitionsin useplain page change
- Scroll-driven animations for the header line and reading progressin usestatic
- WebGL2 for the labyrinth and the mapin useCSS print
- Balanced and pretty text wrappingin usedefault wrapping
Each of these is an enhancement. Where a feature is missing the site falls back quietly. Press G on any page to see the twelve-column grid.
Built with
- Framework
- Astro 7 with MDX, static output, 8 pages
- Motion
- GSAP (core, SplitText, InertiaPlugin), Lenis for smooth scrolling with a mouse, WebGL2, cross-document View Transitions, CSS scroll-driven animations
- Loading
- Speculation Rules for instant navigation, build-time image sizes, lazy media, no web fonts from third parties
- Type
- Mona Sans and Mona Sans Mono by GitHub, under the SIL Open Font License, reduced to three small files with only the axes and characters this site uses
- Process
- Designed by me. Much of the code was written with an AI coding agent: I set direction and made the design calls, the agent implemented, and I checked every step in the browser. The same way I built Klangraum.