
The inchworm menu
Menus, Hover, Transitions
NewFree
A hold-to-sign-in Sign in with Touch ID button with a real sensor in the round end of the pill: a steel ring round a dark sapphire, and on it a fingerprint that is grown, not drawn, with its core, its loops and its delta. Held down, ink fills the ridges from the point touched outwards, running along the ridges faster than it jumps the valleys, with a wet edge; let go too soon and it drains back (Keep holding…). Once read, the ridges fold one after the other into a tick and the pill opens on Signed in as Ada; Lock opens them out 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 fingerprint 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 fingerprint button: Sign in with Touch ID, held down. In the round end of the pill a sensor with a
fingerprint grown from a field of directions (a loop round its core, a delta below). Held, ink fills the
ridges from the point touched outwards, running along them faster than it crosses the valleys; let go too
soon and they empty again. Read, the ridges fold into a tick and the pill opens on the person signed in -->
<div class="touch" data-touch>
<button class="touch__btn" type="button" data-btn data-autoclic>
<span class="touch__fill" aria-hidden="true" data-fill></span>
<canvas class="touch__sensor" aria-hidden="true" data-sensor></canvas>
<span class="touch__faces">
<span class="touch__face" data-face="a">
<span class="touch__word" data-wide="Sign in with Touch ID" data-word>Sign in with Touch ID</span>
</span>
<span class="touch__face" data-face="b" aria-hidden="true">
<span class="touch__word" data-word></span>
</span>
<span class="touch__face touch__face--done" data-face="done" aria-hidden="true">
<span class="touch__who">
<span class="touch__lead" data-lead-text>Signed in as</span>
<span class="touch__name" data-user-text>Ada</span>
</span>
<span class="touch__lock" data-lock-text>Lock</span>
</span>
</span>
</button>
<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.
28 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 fingerprint button” from Animorio and fit it into my siteThe ridges are the lines where a phase comes back to zero. The phase grows with the distance from a stroke that starts at the middle of the print and runs out past its edge (the rings round a segment are nested hairpins: the loop), and under the loop it gives way, through a soft maximum, to wide arches across the finger; where the two families meet the ridges crowd and split three ways, as round a delta. Two octaves of smooth noise make it wander, so the ridges end and fork. The side of the loop and the noise depend on SEED.
On a grid of half a pixel the sine of the phase is worked out; marching squares follows the lines where it is zero and keeps only those where the cosine is positive (the ridges, not the valleys) and that lie inside the slightly ragged outline of the fingertip. The segments are chained into polylines through the cell edges they share, smoothed twice, and the ones shorter than three and a half pixels are dropped. It is done once: every frame only draws the polylines.
At every press each point of every ridge gets a distance: crossing costs CAPILLARY times the straight distance from the finger, running along a ridge costs its length, and two passes along each ridge (forwards and back) keep the shorter. The ink front moves out over that distance while the press lasts (in HOLD seconds, with a pace that slows towards the end) and each segment is cut at the exact point where the front crosses it: behind the front the red, in the last five steps the peach of the wet ink. Letting go, the front goes back.
Each ridge knows its length travelled at every point, from 0 to 1, and is laid on the tick along the same fraction (from its left end), with a small sideways offset that tightens. The ridges start in order of their distance from the middle, with a power3.inOut pace and a turn of e(1−e) round the middle that reads as folding; the colour goes from the ink to green in the second half. At the end the ridges fade and a clean three-pixel tick with round ends remains.
The button captures the pointer (setPointerCapture), so the finger does not lose it if the pill moves, and while the finger is down the pill never narrows. A press shorter than a quarter of a second is a tap and starts a reading that goes on by itself; so do the click of Enter and a script's click (detail 0), and the preview shows the whole round. Space held is the finger: keydown and keyup with preventDefault, so the button does not also make its own click. The click on Lock counts only if the press began with the sign-in already done.
Two faces for a line of words, taking turns (the old one leaves in a blur, the new one comes in after), plus the face of the person signed in. Before every change the tweens still running on the faces are stopped, so two changes close together never leave two lines on top of each other. The pill takes the measured width of the words with back.out. It is a real button: aria-disabled while it signs in and locks, an aria-live region says Reading…, Keep holding…, Signed in as Ada and Locked. Signed out.
| Name | File | Default | What it changes |
|---|---|---|---|
LABEL | script.js | Sign in with Touch ID | The words at rest. If they are too long for the room, the type gets smaller. |
READING | script.js | Reading… | The words while it is held down. |
AGAIN | script.js | Keep holding… | The words when it is let go too soon. |
BUSY | script.js | Signing in… | The words while the ridges fold into the tick. |
LEAD | script.js | Signed in as | The small line over the name. |
USER | script.js | Ada | The person signed in. The sign-in is simulated: in a real site this is the name your server returns after WebAuthn. |
LOCK | script.js | Lock | The small pill that signs out. |
LOCKING | script.js | Locking… | The words while the tick turns back into the print. |
LOCKED | script.js | Locked | The words once signed out, before going back to rest. |
HOLD | script.js | 1.2 | The seconds the finger has to stay down. A reading started by a click takes a third longer. |
SPACING | script.js | 2.8 | The distance between two ridges, in pixels: lower, a denser print. |
CAPILLARY | script.js | 3 | How much slower the ink crosses a valley than it runs along a ridge: 1 it spreads in a circle, 6 it follows the ridges one by one. |
SEED | script.js | 7 | The finger: another number, another print (and the loop on the other side). |
For the sign-in button that uses biometrics: Sign in with Touch ID or with a passkey in a banking app, a password manager, a wallet, the confirmation of a payment or of a delicate action (Hold to confirm). It has to be wired to the WebAuthn API, which asks the system for the finger: here it is all simulated and no sensor is asked anything.
The pill at rest is about 270 pixels wide and the open one about 220: at 390 pixels they fit with room to spare, and if the words are too long the type gets smaller. A finger held on the pill reads, a short tap starts the reading by itself; the long-press menu and the selection are switched off on the button.
No print growing in, no swelling fill or widening words (under the mouse the fill only lightens a little). Held down, the ink fades in on all the ridges at once, and let go too soon it fades away; once read, the ridges fade and the tick fades in in their place, the pill takes its new width while the words are gone and Signed in as Ada fades in. Lock goes back the same way.
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.