Chute

Roll up your message and send it up the tube.

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

Field notes

A contact page that is a pneumatic tube station. The form is a paper note hanging from it: pick a desk on the dial, write, and send. The note rolls into a carrier, rides a glass tube out of the screen and comes back with a receipt, or comes back undelivered with everything you wrote still on it.

  • 3D
  • Pointer interaction
  • Light theme

Chute is the contact page of Desk Nine, a fictional dispatch office. An enamel-green station hangs on a painted wall with a pressure gauge, a signal lamp and a glass tube that climbs away under the top edge of the screen. The form is the note hanging from the station: real inputs with autocomplete hints, labelled for screen readers, and a dial that is an ordinary radio group choosing Sales, Support or Press. Send it and the paper rolls up into the carrier, the gauge rises, and the carrier rides the tube out of the screen. When your backend answers, the lamp turns green and the carrier returns with a receipt: desk, ticket number, reply address. If the request fails, the carrier comes back a third of the way, rattles under a red lamp and drops, and the note unrolls with every word still on it.

The paper is HTML and the station is 3D, kept in register by one number: a world unit is a CSS pixel, and both are scaled by the same factor. A 12-second tour runs until the visitor does something and hands over mid-motion. Field errors appear on the paper before anything moves. Everything works from the keyboard, reduced motion cuts instead of rolling, and without WebGL the page is the same note on a plain green page. The backend seam is one function in one file, send.ts; the demo sends nothing anywhere.

How it's built

  • HTML paper on a 3D station with no perspective maths: the camera's distance is set so one world unit is one CSS pixel on the wall, and the note's container gets the same scale factor. The station is built in pixels, so the cap sits exactly where the paper starts.
  • One frame from one clock: the page is a Frame computed from a small state that keeps when things happened (sent, reply landed, write another), never how far along they are. The tour, the recorder and a visitor share the code, so frame 0 equals frame 12 and the first click adopts the tour mid-ride.
  • A ride that waits for your server: the carrier leaves when you send and the way back starts when the reply lands, but never before the carrier is out of the screen. A fast answer and a slow one both look right, and a failure plays on the way back.
  • Paper that rolls with CSS: the note is clipped from the bottom with clip-path while a roll-shaped bar rides the clip edge and thickens, then hands over to a 3D roll inside the carrier. The form and the receipt swap only while the paper is fully rolled.
  • Glass without a grey fog: the tube's material is near black with additive blending, so it adds only its reflections of three's RoomEnvironment. The tube is rebuilt from a curve on every resize, and the carrier's position is a fraction of its length.

Inside the prompt

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

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

Recut it

  • Wire it to your backendsrc/lib/send.ts → send()
  • Rename the product, the desks and every stringsrc/lib/copy.ts + index.html → PRODUCT / DESKS / COPY / <title>
  • Change the field rulessrc/lib/validate.ts → validate() / MIN_MESSAGE
  • Recolour the station and the lightssrc/lib/look.ts → MATS, LIGHT, LAMP
  • Recolour the paper and change the fontssrc/index.css + index.html → @theme
  • Retime the ride, or the toursrc/lib/timeline.ts → ROLL, LEAVE, RETURN / TOUR_SEND, TOUR_LAND, TOUR_AGAIN
  • Add a fieldsrc/components/Note.tsx + src/lib/validate.ts → <form> / Fields
  • Reroute the tube or reshape the stationsrc/scene/build.ts → route() / STATION

Also in the vault