Shaft

A 404 page stuck between floors.

Fig. 1 — Shaft, live captureOpen the live demo ↗12 s loop · recorded frame by frame on GPU

Field notes

A 404 page that is a lift car stuck between floors. The indicator reads 404, the floor buttons are your real links, and pressing one rides the car down and opens the doors before the page leaves. Pry the doors apart to see the shaft wall, or ring the alarm. Nobody is coming.

  • 3D
  • Pointer interaction
  • Dark theme

Shaft is the not-found page of a building that has no floor 404. You stand inside a brushed-steel lift car facing the doors. The indicator above them reads 404 and flickers, a notice plate on the left says what happened, and the panel on the right holds your links as floor buttons: real anchors, so ctrl-click, middle click and the keyboard all work. A plain click lights the button, shakes the car, runs the indicator down to that floor and opens the doors on a lit lobby, and then the page goes there. Drag the doors apart with a mouse or a finger and the shaft wall is behind them, with 404 stencilled on the brick. The alarm button turns the lamp red and helps nobody.

Every pose comes from one pure function of a small state and the time, so the car, the indicator, the status line and the lit button cannot drift apart, and the 12-second recording loops exactly. On a phone the notice becomes a strip across the top and the buttons grow to 44 px. Reduced motion removes the shaking and flicker and makes every floor an ordinary link; without WebGL the page is the same heading and links on a plain dark page. The floor list is one array in copy.ts.

How it's built

  • Copy on 3D plates without CSS perspective: the camera faces the door wall head-on, so one scene unit there is a fixed number of pixels. The layout hands the same boxes to the meshes and, as CSS variables, to the HTML laid over them.
  • One frame from one clock: the car is a Frame computed from a small state that keeps when things happened (floor pressed, alarm rung, doors let go), never how far along they are. The recorder and a visitor share the code, and a Node check proves frame 0 equals frame 12.
  • A ride is a round trip. The page leaves when the doors are open; if the link leads back to the same page, the car closes its doors, climbs back to 404 and is stuck again with no reset code. A visitor returning through the back/forward cache finds it stuck at once.
  • Floors are plain anchors. Only an unmodified left click on a stuck car is intercepted; a second click, a modified click, reduced motion and the no-WebGL page all fall through to the browser.
  • Flash-safe by test: the alarm turns the light red once and holds it, pulsing only its strength at 1.5 Hz and by 40% at most, with the flicker switched off meanwhile; the check fails if any of that changes, or if the stuck lamp ever flickers more than three times in a second.
  • Steel without image files: brushed metal is a canvas of random grey columns used as roughness and bump, the shaft wall and the lobby sign are canvas textures drawn with the page's fonts, and reflections are three's RoomEnvironment.

Inside the prompt

Every file below is written out in full, so your agent creates the project exactly — no guessing, no missing pieces.

shaft-404/
├─ .gitignore
├─ check.ts
├─ index.html
├─ package.json
├─ tsconfig.json
├─ vite.config.ts
└─ src/
   ├─ App.tsx
   ├─ index.css
   ├─ main.tsx
   ├─ vite-env.d.ts
   ├─ components/
   │  ├─ Car.tsx
   │  └─ Scene.tsx
   ├─ lib/
   │  ├─ copy.ts
   │  ├─ live.ts
   │  ├─ look.ts
   │  ├─ loop.ts
   │  ├─ time.ts
   │  └─ timeline.ts
   └─ scene/
      ├─ build.ts
      ├─ draw.ts
      └─ stage.ts

Recut it

  • Set your own floors and linkssrc/lib/copy.ts → FLOORS
  • Rewrite every stringsrc/lib/copy.ts → PAGE / STATUS
  • Show another error numbersrc/lib/copy.ts + index.html → LOST, and the 404 in PAGE, STATUS and <title>
  • Recolour the steel, the lamp and the wall paintsrc/lib/look.ts + src/index.css → MATS, LIGHT, PAINT / @theme
  • Make the ride shorter or longersrc/lib/timeline.ts → DOWN, OPEN, GO_AT, CLOSE, UP, RIDE_DUR (move them together)
  • Calm or remove the jolts and the alarmsrc/lib/timeline.ts → JOLT_EVERY, ALARM_HZ, ALARM_SWING
  • Move the plates or change the phone layoutsrc/scene/stage.ts → WIDE / TALL
  • Reshape the car or the lobbysrc/scene/build.ts → CAR / buildRig()

Also in the vault