Skip to content
animorio
IT
All components

The turnstile button

Buttons, Physics, 3DCanvas, GSAPAdvancedCompressed weight: Demo 14 KBGSAP 28 KBFreeDownload the zip

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.

The code

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>

Playground

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

Or ask Claude Code for it

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 site

What it does

On a black page a graphite pill, 60 pixels tall. In its round left end there is a dark hole, and out of it comes the head of a tripod turnstile: a steel drum hub with its axis tilted towards you and to the right, and three tube arms set on a cone round the axis, one of them level and pointing at the words, like the one that bars the way. The arms are brushed steel: bright where the light from the upper left catches them, dark on the far side, with the fine lines of the brushing along them, the collar where they go into the hub and a soft shadow on the pill. In the middle Sign in in Mona Sans; in the right end a reader of dark glass with the sign of the waves. With the mouse near, the whole head leans to the pointer and the arms give a few degrees, as under a hand, and go back with a small shiver when the pointer leaves; over the button the words widen on the wdth axis and the fill swells with a bounce. At a click a small ivory pass with a red band comes down and taps the reader, the words become Signing in… with a sheen running through them, the three waves light up in amber one after the other; then the pass goes, a green arrow takes the place of the waves and the arm is let go: it gives back a hair, the hand pushes it and the tripod turns a third of a turn, slowing on its bearing, hits the stop, bounces back and the detent takes it with a last shiver, while the pill gets a small knock. The pill widens on Welcome back, with Noor under it, and a small Log out pill. The second click: Signing out…, the arrow leaves, the tripod turns back a third of a turn and locks again with its knock; Signed out, and after a moment Sign in again.

How it works

A real tripod, in 3D

The 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.

Arms of brushed steel

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.

Shadows falling on the pill

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.

Inertia, stop and detent

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 reader and the words

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.

Values to change

NameFileDefaultWhat it changes
LABELscript.js'Sign in'The words at rest.
BUSYscript.js'Signing in…'The words while the reader reads the pass.
LEADscript.js'Welcome back,'The small line over the name, once in.
PERSONscript.js'Noor'The name, once in (invented: nothing is sent anywhere).
LOGOUTscript.js'Log out'The little pill that signs out.
LEAVINGscript.js'Signing out…'The words while it turns back.
GONEscript.js'Signed out'The words once out, before Sign in comes back.
READscript.js0.9The seconds the reader takes to read the pass.
PUSHscript.js5.5How fast the hand pushes the arm round, in radians a second.
FRICTIONscript.js1.6How much the bearing slows the tripod as it turns.
BOUNCEscript.js0.38How much of its speed the tripod keeps when it hits the stop and bounces back.
FEELscript.js1How much the arms feel the pointer: 0 they stay still, 2 they lean a lot.

Try them in the Playground

When to use it

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.

On phones

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.

With reduced motion

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.

Pitfalls

  • Seen almost along the axis the tripod looked like a flat star, a logo: the axis is turned well to the right and a little up, so the hub shows from the side as a drum and one arm is level, pointing at the words, as in real turnstiles.
  • Pushed with a single blow the tripod made its third of a turn in three frames and could not be seen: a hand brings it up to speed for a fifth of a second, then it slows on the bearing, and if it is too slow the hand goes with it to the stop.
  • The canvas blur filter is missing in Safari before version 18: the shadows are drawn off the canvas with an offset canvas shadow, which lands alone and blurred.
  • The loop went to sleep only with the pointer out of the window, and a mouse resting on the button kept it awake for ever: it sleeps once the lean has got where the pointer wants it.
  • Signing out, the pill measured the words Signing in… instead of Signing out… and the words touched the reader: the text is changed before measuring.

Libraries

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.

Testing

Tested on 12 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

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.

All components