Every post is a canvas
This blog doesn’t use templates. Each post is a hand-designed page where the art is the design. The shader you see above is a fragment shader running in a WebGL2 canvas — layered fractal Brownian motion with a monochrome palette and film grain.
How it works
The art system is built on Astro’s islands architecture. The page itself is static HTML and CSS. The shader component is a React island that only hydrates when it scrolls into view:
<ShaderCanvas client:visible fragmentSource={shaderSource} />
This means:
- Content renders immediately from static HTML — no waiting for JavaScript
- Shader loads lazily — only when the canvas enters the viewport
- Zero JS on pages without art — Astro ships no JavaScript by default
- Each post’s dependencies are code-split — visiting this page doesn’t load Three.js or any other post’s art
Graceful degradation
The shader canvas handles failure at every level:
- No WebGL? Falls back to a CSS gradient
- Shader compile error? Same fallback
- Context lost mid-render? Catches the event, shows fallback
prefers-reduced-motion? Renders a single static frame- Tab hidden or scrolled away? Animation pauses to save resources
Content is always readable regardless of what happens to the art.
The two-file system
Each post consists of two files:
- Content file (
.mdx) — the writing, with frontmatter metadata - Page file (
.astro) — the bespoke design that imports the content
The art dictates the theme. This post declares theme: dark in its frontmatter, so the entire page uses the dark palette. The theme toggle on the home page doesn’t apply here — the art is the design decision.