Walk in (desktop or phone; scroll to walk)
The Magic Theatre is from Hermann Hesse’s Steppenwolf (1927): a sign over an old door that isn’t there by day, “Magic Theatre. Entrance not for everybody. For madmen only”, and inside a corridor of doors, each with its own scene behind it, and a great mirror at the end. I built it as a hidden corner of my personal site. Normally the way in is a keyhole at the foot of my Now and Journey pages that only appears at night (9 pm to 5 am, your time). Add ?theatre to see it during the day: Now · Kartik Gautam
You scroll to walk the corridor. Knock on a door and it swings open on its room, and you walk straight in, all in one move. The mirror is at the end.
Some of what went into it:
- No models and no texture downloads for the place itself. All the geometry is built in code, and every surface is painted into canvas textures from a few seeded noise helpers: damask wallpaper with water damage and peeled seams, walnut panelling, floorboards, the runner, stone, rusted iron, brass, the stele in each room. They’re seeded, so a door looks the same on every visit.
- Nothing stalls the page. Painting all of that takes a couple of seconds, so the painters are generators that yield between rows and run a few milliseconds at a time while the page is idle. Shaders are compiled ahead of time a few objects per frame (in parallel where the browser supports it), and textures go up to the GPU a few at a time, so the first frame doesn’t hitch.
- The rooms really are behind the walls. While you’re in the corridor, the room behind the door you knocked on is rendered from your own eye and shown in the doorway as a portal, so you see the shrine and its candles as the leaf swings. Then the camera walks through, and from the doorway on it sees only the room. Both views come from the same eye, so there’s no cut.
- Doors on springs. The target opening eases in and the leaf follows it on a spring, so an old door is slow to start, and a light push overshoots a little and settles. Candlelight leaks round the leaf, under it and through the keyhole.
- The neon sign is one tube per letter (troika text), so each letter can flicker and fail on its own. The bulbs and tubes light the sign in the shader instead of with real lights: one soft source per run of three bulbs and one per letter, unrolled with constant indices, which every GPU handles better than a loop over a uniform array (software renderers especially).
- The mirror renders the corridor every frame from your eye reflected through the glass, broken across a couple of dozen shards, each at its own small angle and tint. It lies, though: you aren’t in it, it shows where you stood a moment ago, and a lamp that gutters in the corridor burns steadily in the glass. Stay a while and your breath fogs it from the inside. If you let it use your camera, the video becomes a texture, and a few times a second a small copy is rendered into a ring of cells in one render target, so some shards show you a few seconds late. All of it stays on the GPU: nothing is read back, stored or sent anywhere.
- Post and performance: bloom, a little chromatic aberration, noise and a vignette (@react-threereact-three/postprocessing). drei’s PerformanceMonitor lowers the DPR on slow devices, and phones get a lighter mode: no floor reflections, four real lights, less dust and fog. Without WebGL there’s a hand-drawn 2D version (
?mode=2d). - The rooms’ text isn’t bundled into the page. Each room is fetched only when you knock.
Stack: t@react-threeree r169, @react-three/fiber, drei, @react-three/postprocessing, Next.js. About 21k lines of TypeScript for the 3D part.
Source: GitHub - atomsmasher81/react-portfolio: A portfolio build on react · GitHub (the 3D part is in src/components/v2/theatre3d).
I’d love to hear what breaks on your device, and what you’d do differently with the mirror.



