
The time wheel
Pickers, 3D, Physics
NewFree
The Sign in button with the head of a tripod turnstile in the round end of the pill: a hub and three arms of brushed steel drawn in real 3D on a canvas, in perspective, and a card reader of dark glass in the other end. The arms feel the pointer and lean a little. At a click a small pass taps the reader (Signing in…), the reader's waves light up, a green arrow shows, the arm is let go with a jolt and the tripod turns a third of a turn on its inertia, hits the stop and bounces back before the detent catches it; then Welcome back, Noor. The second click (Log out) turns it back the other way and it locks again.
A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>The turnstile button</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the turnstile button: a Sign in pill with the head of a tripod turnstile in its round left end, three arms of
brushed steel drawn in perspective on a canvas, and a card reader in its right end. The arms feel the pointer
and lean a little; a click taps a pass on the reader, the top arm is let go and the tripod turns a third of a
turn with its inertia, bouncing on the stop. A second click signs out: it turns back and locks again -->
<div class="gate" data-gate>
<button class="gate__btn" type="button" data-btn data-autoclic>
<span class="gate__fill" aria-hidden="true"></span>
<canvas class="gate__rotor" aria-hidden="true" data-rotor></canvas>
<span class="gate__faces">
<span class="gate__face" data-face="rest">
<span class="gate__word" data-wide="Sign in" data-rest-text>Sign in</span>
</span>
<span class="gate__face gate__face--busy" data-face="busy" aria-hidden="true">
<span class="gate__shine" data-busy-text>Signing in…</span>
</span>
<span class="gate__face gate__face--done" data-face="done" aria-hidden="true">
<span class="gate__who">
<span class="gate__lead" data-lead-text>Welcome back,</span>
<span class="gate__name" data-name-text>Noor</span>
</span>
<span class="gate__out" data-out-text>Log out</span>
</span>
<span class="gate__face gate__face--busy" data-face="gone" aria-hidden="true">
<span class="gate__gone" data-gone-text>Signed out</span>
</span>
</span>
<!-- the reader: a pane of dark glass with the contactless sign, and the arrow that lets you through -->
<span class="gate__reader" aria-hidden="true">
<svg class="gate__waves" viewBox="0 0 24 24" data-waves>
<path d="M8.6 8.2a5.4 5.4 0 0 1 0 7.6"/>
<path d="M11.8 5.4a9.4 9.4 0 0 1 0 13.2"/>
<path d="M15 2.6a13.4 13.4 0 0 1 0 18.8"/>
</svg>
<svg class="gate__arrow" viewBox="0 0 24 24" data-arrow>
<path d="M4.5 12h14M13 6.2l5.8 5.8-5.8 5.8"/>
</svg>
</span>
</button>
<!-- the pass that taps the reader -->
<span class="gate__pass" aria-hidden="true" data-pass>
<span class="gate__band"></span>
<span class="gate__chip"></span>
<span class="gate__lines"></span>
</span>
<p class="sr" aria-live="polite" data-live></p>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.
27 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “The turnstile button” from Animorio and fit it into my siteThe rotor has a frame of its own: the axis is its z, and the three arms sit on a cone round the axis, a third of a turn apart. Each point goes to the page's frame through two rotations (the axis lifts and turns to the right) and then to the canvas in perspective: the scale is the eye's distance over the point's distance. The hub is two rings projected point by point; its side is their convex hull.
Each arm is a capsule: a stroke with round ends, as wide as the tube in perspective. Its gradient runs across it; the part of the light square to the arm decides where the highlight sits and how bright it is, so the highlight slides as the tripod turns. On top, seven fine lines along it make the brushing. The arms are drawn from the farthest to the nearest, sorted every frame.
The shadows of the hub and the arms are their points moved along the light, the more the higher they stand over the pill. They are drawn a thousand pixels off the canvas with a canvas shadow offset by as much: only the blur lands on the canvas, soft, with no filters.
The rotor is a body with inertia. Locked, a stiff spring holds it in its detent with little damping, and the pointer moves its resting point by a few degrees. Let go, a hand brings it up to speed for a moment, then the bearing slows it; when it reaches a third of a turn it hits the stop, bounces back with part of its speed (BOUNCE) and the next detent takes it.
The pass comes down, taps and leaves on a GSAP timeline; the reader's waves are three SVG arcs that light up in turn, then give way to a drawn arrow. The pill measures its faces and widens with a bounce; the old words leave before the new ones come, so two lines never sit on top of each other.
| Name | File | Default | What it changes |
|---|---|---|---|
LABEL | script.js | 'Sign in' | The words at rest. |
BUSY | script.js | 'Signing in…' | The words while the reader reads the pass. |
LEAD | script.js | 'Welcome back,' | The small line over the name, once in. |
PERSON | script.js | 'Noor' | The name, once in (invented: nothing is sent anywhere). |
LOGOUT | script.js | 'Log out' | The little pill that signs out. |
LEAVING | script.js | 'Signing out…' | The words while it turns back. |
GONE | script.js | 'Signed out' | The words once out, before Sign in comes back. |
READ | script.js | 0.9 | The seconds the reader takes to read the pass. |
PUSH | script.js | 5.5 | How fast the hand pushes the arm round, in radians a second. |
FRICTION | script.js | 1.6 | How much the bearing slows the tripod as it turns. |
BOUNCE | script.js | 0.38 | How much of its speed the tripod keeps when it hits the stop and bounces back. |
FEEL | script.js | 1 | How much the arms feel the pointer: 0 they stay still, 2 they lean a lot. |
For the sign in of an app that opens a door, real or not: a gym, a coworking space, an office, an event, a members' area. It stands alone on a dark background; on a real site the call that really signs in goes where the reader's pause is.
The pill fits the screen (at most the screen's width less 32 pixels) and the tripod keeps its size. A finger resting on it acts as the pointer: the head leans to the finger; the tap starts the whole cycle.
No turning and no leaning: a third of a turn brings the tripod back to the same picture, so it stays still. The pass fades in on the reader, the waves and the arrow change with a fade, the pill takes its width while the words are faded out.
GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 14 KB; the libraries: GSAP 28 KB.
Tested on 12 October 2026
Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.