Small on purpose
- Updated
You clicked the number in the footer. Your browser measured that line, I didn’t type it: how many bytes the page took and how long it took to arrive. This is why it’s small, and how it stays that way.
The budget
Every page on this site has to fit in 14 KB after compression, and it may not fetch anything else while it loads. Not a font, not a stylesheet, not a script. One request, then you’re reading.
14 KB isn’t a round number I happened to like. It’s roughly what a server can send before it has to stop and wait for the browser to say “got it”. Stay under it and the whole page arrives in the first round trip. Go over and you pay for a second one, which on a bad connection can be most of a second.
A small script checks every page before it ships and fails the build if one is over budget or pulls in a resource during load. The rule is enforced, not remembered.
On the wire
- Index
- 8.4 KB, compressed. 20 KB raw.
- Logo shader
- 3.3 KB, loaded after the page is done.
- Now, Agents
- 3.6 KB and 4.0 KB.
- The previous index
- 21 KB of HTML plus 170 KB of JavaScript, 24 KB of web fonts and 104 KB of images, in 43 requests. Lighthouse had it interactive at 2.2 s on a simulated phone; this one at 0.8 s.
Same pages, same words, built twice. The previous version was a lean Next.js and React build; this one is hand-written HTML. Lighthouse on a slow simulated phone, the day the old one was switched off:
- React
- 17 to 43 requests per page, 200 to 365 KB on the wire, 150 to 170 KB of it JavaScript. One to two seconds before you could tap anything.
- Hand-rolled
- 2 requests, 5 to 10 KB, no JavaScript in the way. Under a second.
Measured today with compression on. Your footer will say something close.
How
- One file per page. The CSS lives in the page, about forty lines of it. No stylesheet to fetch, no cache to warm.
- No framework and no build step. The HTML is written by hand, and what you see in view-source is what I wrote.
- System fonts. Whatever your device already has is good enough, and it costs nothing.
- Icons are SVG paths pasted inline. A few hundred bytes each, and they take the text colour, so dark mode is free.
- Dark mode is five CSS variables and a media query.
- Photos only where they carry the story, like the clover tattoo. They load lazily as WebP below the fold, so they never count against the first round trip.
- The logo is the one indulgence. The liquid-metal effect is a 3 KB WebGL shader that loads after the page is done, and it builds its texture from the SVG already in the page instead of downloading an image. If your system prefers reduced motion, or has no GPU to run it on, it stays a still SVG.
- Links to other pages here are prerendered when you hover, so the next page is usually already there.
- Static files on Vercel’s edge, compressed with brotli.
- The footer measures itself with the browser’s Navigation Timing API. If it says “cache”, your browser didn’t need to ask twice.
Clean
Small is half of it. The other half is that nothing happens here that you didn’t ask for. Open the developer tools on any page and it should be boring.
- No cookies. Not one, first-party or otherwise. There is nothing to accept, so there is no banner asking you to accept it.
- No analytics, no pixels, no fingerprinting. I don’t know you were here. If you want me to know, there’s an email address on the index and a human reads it.
- No third-party scripts. The only code that runs is the footer line that measures itself and, on the index, the logo shader. Both are a few lines you can read in view-source.
- No console errors and no warnings. A red line in the console is a bug I shipped, and I count it the same as a page that’s over budget.
- No requests you can’t account for. One for the page. One for the shader on the index. One for each photo as you scroll to it. Nothing fetched because a library thought it might need it later.
- No popups, no newsletter modal, no “we value your privacy”. The page is the page.
Checked
Every push runs two checks before anything ships. The budget script above, and Lighthouse over every route, which has to score 100 in Performance, Accessibility, Best Practices and SEO. After each deploy the live site is audited again, from a machine that isn’t mine.
Why
The previous version of this site ran on Next.js and React. It was fine. It was also 170 KB of JavaScript, plus fonts and images, to show you a few paragraphs and a list of links, and it needed a build pipeline to change a comma. It was switched off on October 16, 2026, measured one last time the same day; those numbers are the ones above.
I built these pages for people on slow connections first. It turned out the same thing suits machines: an agent fetching this site with one question and a token budget wants plain markup and facts stated outright. So every page has a markdown twin, there’s a map at /llms.txt, /agents gives machines the short version, and /readable gives you the long one.
And honestly, it’s a pleasure. A budget you can’t exceed turns every byte into a decision, and I like making those. The whole site is a folder of HTML files I can read top to bottom. No dependencies to update, nothing to migrate next year.
The trade
There are no components, so the CSS at the top of each page is copied by hand and kept in sync by me. With a dozen pages that’s a non-problem. With a hundred it would be a different site, and I’d build it differently.
The pieces are on display at /design-system. If you’re a machine, /agents has what you need.