Tumbler

Sign in by opening the vault.

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

Field notes

A sign-in and sign-up page that is a bank vault door. The form is stamped on the door: typing turns the wheel, a stronger password throws more bolts, and a good submit swings the door open with the form on it and takes you into the strongroom. Or grab the wheel and turn it yourself.

  • 3D
  • Pointer interaction
  • SaaS
  • Dark theme

Tumbler is the sign-in page of Strongroom Seven, a fictional place to keep things. A steel wall fills the screen and a round vault door sits in it, with the form on its face plate: real inputs with the autocomplete hints password managers look for, labelled for screen readers. Every character you type turns the wheel a notch. On sign-up the bolts start drawn back and are thrown as the password gets stronger. Submit with the button, with Enter, or, on a screen wide enough to show the whole ring, by grabbing the brass grips and turning it half a round. A refused password shakes the door and the form together. An accepted one spins the wheel, draws the bolts and swings the door open with the form still on it, then the camera goes through into a strongroom where one brass deposit box carries your name.

The form and the door are one object: the page shares the 3D camera's perspective with CSS, so the form swings, spins and shakes with the steel. Switching between sign in and sign up spins the face plate one turn. A 12-second tour runs until the visitor does something and hands over mid-motion. Everything works from the keyboard, reduced motion cuts instead of easing, and without WebGL the page is the same form on a plain dark page. The backend seam is two functions in one file, auth.ts; the demo sends nothing anywhere.

How it's built

  • A form glued to a 3D door: the page gets a CSS perspective equal to the camera's distance in pixels, and the form's transform repeats the door's hinge, swing, spin and shake from the same numbers the meshes read. backface-visibility hides it once the door passes 90 degrees.
  • One frame from one clock: the door is a Frame computed from a small state that keeps when things happened (accepted, refused, locked, wheel let go), 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-motion.
  • A wheel you can turn: the ring's hit area is worked out from where it appears on screen, a drag adds its angle, and half a round calls form.requestSubmit(), so the wheel runs exactly the checks the button runs. Let go, and a closed-form coast-and-settle brings it back.
  • Steel without image files: brushed metal is a canvas of random grey rows used as roughness and bump; on a lathe the rows run round the axis, so they read as circular brushing. Reflections are three's RoomEnvironment. The deposit box's name tag is a canvas texture.
  • The backend seam is two async functions. Resolve to open the door, throw to shake it. The demo accepts everything except the password wrong and sends nothing.

Inside the prompt

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

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

Recut it

  • Wire it to your backendsrc/lib/auth.ts → signIn() / signUp() / onAccepted()
  • Rename the product and rewrite every stringsrc/lib/copy.ts → PRODUCT / COPY / BOX
  • Skip the strongroom and redirect at oncesrc/lib/copy.ts → STRONGROOM
  • Change the password rulessrc/lib/strength.ts → RULES
  • Recolour the steel, brass and lightssrc/lib/look.ts + src/index.css → MATS, LIGHT / @theme
  • Retime the opening, or the toursrc/lib/timeline.ts → SWING, FLY, OPEN_DUR / TOUR_ACCEPT, TOUR_LOCK
  • More or fewer boltssrc/lib/timeline.ts → BOLTS
  • Add a fieldsrc/components/Door.tsx → <form>
  • Reshape the door or the strongroomsrc/scene/build.ts → DOOR / buildRig()

Also in the vault