The homepage: a beige CRT computer on a wooden desk in an oak-panelled study at golden hour, with a green banker's lamp, a card catalog, a legal pad, and an opened laptop with a soldering station on the left

Part I and Part II are about the old version of this site. This one’s about the latest iteration, or rather, the new “immersive”, 3D facade, created with CC.

Felix Rieseberg turned his site into a 3D room and wrote about it in Made by mechanical means. Claude built the whole thing, driving Blender with python in the cloud, while Felix mostly left notes on the renders. I wanted one immediately, so I pointed Claude at this repo, told it to read Felix’s post and follow his process, and asked for a dim room at night with rain on the window and my ThinkPad on the desk, running a terminal UI that would be the menu for the whole site.

The first version was live in about a day. I haven’t written a line of code since, every commit since then is Claude’s except me merging PRs. What I do is look at renders, say what’s off, and then I merge it when I’m happy and nothing is breaking (nothing goes live until I do this, and I have a preview site on running on my server).

How It Works

It all happens in a Claude project, which is basically a group chat with threads. I say what I want in the main chat and Claude starts a thread for each piece (the room, the computer, the card catalog, hosting, etc. and so on), each one its own Claude on its own branch.

The 3D part is Felix’s recipe. Every object is a python script run in a headless Blender session (vs. astra using it locally), at real-world scale. Then Cycles (Blender’s path tracer) bakes the lighting into textures, one layer per light, since a browser can’t do that kind of soft bounce light in real time. In the browser a three.js shader adds the layers back up, which is how the lamps switch on and off and the sun “sets” without re-rendering anything:

// a dim filament is a warm one: as the lamp comes up or fades, blue lags red
vec3 lamp = texture2D(uLamp, vUv1).rgb * uScales.x * pow(vec3(uLampOn), vec3(1.0, 1.25, 1.6));
...
vec3 irr = lamp + scr + amb;
// the window's sky and the sun's bounce, baked at golden hour, as much as is left of them now
irr += texture2D(uDayMap, vUv1).rgb * uDayScale * uDayTint;

That first comment is why the desk lamp goes a little orange when you turn it on, like an old bulb.

From Night Room to Study

The ThinkPad went first, since a super detailed Lenovo on my homepage seemed like a licensing problem waiting to happen. It became a generic laptop and then a beige CRT on a desktop case with a Model M style keyboard, which I like way more. A hacking corner showed up for my coreboot post, and a card catalog for my bookshelf, since a card catalog was basically a physical search engine, and it brings with it middle-school nostalgia.

Four renders of the desk: a generic laptop by a rainy window at night, a beige CRT computer in the same spot, the CRT with a hacking corner and an Apollo poster, and the oak study after dark with a green banker's lamp

Laptop, CRT, the night room, and the study after dark.

But I kept worrying it looked like a straight copy of Felix’s, with the rain and the window in the middle and the lamp on the left and all that. I mean, the lamp on the left, the books on the right. Claude - as diligent as ever - effectively scaped Felix’s blog post, nabbed the screenshots from there, and said ‘yeah - let’s do it!’ So now it’s a study with oak panels, books, a globe, a green banker’s lamp and a window off to the side. There’s no rain anymore, you show up at golden hour and the sun sets over about six minutes until it’s night.

Stuff I Like

  • My practice pad. I keep a hard rubber pad and marching sticks in my office, so there’s one in the room now. Tap with two fingers and you’ve got two hands, so real rudiments work, and it’ll play you an original solo, composed by Claude. It’s OK - not my favorite, and I’ll probably swap it out with something of my own at some point.
  • A Game Boy style handheld on the desk (modeled on a ModRetro Chromatic, no logos) with falling bocks (aka Temu Tetris), snake, breakout and minesweeper.
  • A server rack in the corner with a VR headset on top (the headset was crafted using reference photos of the oculus rift). Put it on and you can watch the Apollo 11 landing, my favorite, or try to land the LM yourself. It’s more-or-less impossible, tbh.
  • A street outside the window with brownstones and traffic.
Looking down at the practice pad with two drumsticks resting on it, a music stand with an exercise book and a digital metronome set to 100 behind it, and spare sticks in a bag on the right
Close-up of the teal handheld on the desk next to the mouse, its screen showing a menu: Stacks, Snake, Bricks and Mines

Some Difficulties

At one point I noticed the rain drops on the window were dripping “up” haha. glTF’s texture coordinates run down the glass and the shader assumed they ran up:

   vec4 beads = texture2D(uMap, vUv);
+  float up = 1.0 - vUv.y;                           // glTF's v runs down the glass
...
-    vec2 d = vec2(vUv.x - path(y, lane, seed), vUv.y - y) / vec2(r, r * 1.25);
+    vec2 d = vec2(vUv.x - path(y, lane, seed), up - y) / vec2(r, r * 1.25);

Baking (rendering all the lighting into the textures ahead of time because a browser is way too slow to do it live) the whole room takes an around hour or so, so Claude tried starting helper threads to bake in parallel. They refused to run the bake script because a different Claude wrote it, even after I told them to run it. Fair enough I guess 🤔

Immersive, Plain and the Onion

The 3D room (“Immersive”) is only the homepage. Everything else is still a regular hugo site (“Plain”), so old links work. The onion defaults to Plain, and since Tor Browser doesn’t do WebGL 2 you get a photo of the room there that you can still click around in. If your browser renders WebGL in software you get Plain too, the room would peg your CPU (stole that from Felix - not my idea!).

As for Part II’s “next steps”, the onion still talks to nginx over TCP and there’s still no LaTeX. And it’s a docker image on a DigitalOcean droplet now instead of Linode. Best laid plans, as it were.

Big thanks to Felix. His post is basically my whole approach here, and a few smaller things like the “drag to look around” are straight from his website. go check it out if you haven’t.