Skip to content
animorio
IT
All components

The shop sign button

Buttons, Physics, HoverCanvas, GSAPAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

The Sign out button of an account, painted like the door of a small shop: bottle green, gilded words, and in the round end of the pill the Open / Closed sign hanging from a brass nail on a loop of twine. The sign is a rigid body that swings on the nail and turns on itself; the twine is two strands of verlet points. The pointer brushing past sets it swinging; at a click the sign goes round more than once, the strands twist and lift it, it winds back and forth a couple of times and stops on Closed: Signing out…, then See you soon, Ada, and the door goes dark. The second click turns it back to Open: Welcome back, Ada.

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 shop sign button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the shop sign button: the Sign out button of an account, painted like the door of a small shop, with the Open /
         Closed sign hanging from a brass nail in its round left end on a loop of twine. The sign is a rigid body that
         swings, the twine is two strands of verlet points: the pointer brushing the sign sets it swinging, a click
         spins it round on the nail, the strands twist and lift it, it winds back and forth and stops on Closed -->
    <div class="shop" data-shop>
      <button class="shop__btn" type="button" data-btn data-autoclic>
        <span class="shop__fill" aria-hidden="true"></span>
        <canvas class="shop__sign" aria-hidden="true" data-sign></canvas>
        <span class="shop__words" data-words>
          <span class="shop__word" data-word>Sign out</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>

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.

29 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 shop sign button” from Animorio and fit it into my site

What it does

On a white background a bottle green pill 60 pixels tall, like the painted door of a shop, with a gilded line inside its rim and a soft shadow under it. On the right, in pale gold Mona Sans, Sign out; in the round left end a brass nail and, hanging from it on a loop of twine, a cream enamel sign 60 pixels wide with two brass eyelets, a green frame and the word OPEN in a full green serif. The sign throws a soft shadow on the door. On arrival the sign, left a little crooked, swings itself straight. The pointer coming onto the pill gives it a small push and the sign swings; passing over it brushes it and it swings more, the twine slackening and pulling taut; the words widen and the fill swells with a bounce. At a click the words become Signing out…, the sign goes round a turn and a quarter about the nail: it is seen in perspective, the nearer edge a little taller, it narrows on the cosine until it shows its thickness and then its back, with CLOSED in red. The two strands of twine twist round each other from the nail down and lift it a few pixels; the twisted twine brings it back, the sign turns back and forth less and less and stops on CLOSED as the door goes dark and the words become See you soon, Ada, then Sign in. The second click spins it the other way: Signing in…, the door turns green again, the sign stops on OPEN and Welcome back, Ada arrives, then Sign out again. With the keyboard the focus shows a green ring and the sign swings a little; Enter or Space spin it. On a phone a tap spins it and a finger dragged over it sets it swinging.

How it works

A rigid body with two motions

The sign has two degrees of freedom, worked out in steps of 1/240 of a second with the frame capped at 1/30: the swing θ on the nail, in the plane of the door (θ'' = −ω² sin θ − c θ', ω = 2π · SWING, with a soft stop past 0.26 radians so it never leaves the pill or covers the words), and the turn φ about the vertical through the nail. The twine is the torsion spring: φ'' = −ωt² (φ − φ0) − 2ζ ωt φ', where φ0 is the turn at which the twine is untwisted and ωt = 2π · TWIST.

The click's spin, worked out first

The click moves φ0 on by half a turn (the loop turns on the nail) and gives the sign a speed. Which speed makes it go exactly TURNS turns before the twine stops it? The script runs the spring forward and finds the speed by bisection (forty tries, a few milliseconds). So the sign always goes a little past, comes back with its damping and stops on the right face, whatever TWIST and DAMPING are.

The twine: two strands of verlet points

Each strand has ten points in three dimensions, pinned to the nail and to the eyelet, which comes from the sign (±15 · cos φ across, ±15 · sin φ in depth, turned by θ). Verlet integration with gravity and drag, fourteen rounds of slightly slack length constraints: the strands lag and slacken when the sign moves. When the sign is turned, the points near the nail are pulled onto a helix: the two strands wrap round each other over a length that grows with the twist, and the sign rises because the open V gets shorter (drop = b + √((L − b)² − 15²)).

The sign in perspective, in strips

The two faces are painted once on off-screen canvases at twice the density. Every frame the sign is drawn in 28 vertical strips: each strip has its own depth z = u · sin φ and its own scale f / (f − z), so the near edge is taller than the far one. With cos φ negative the back is used, its columns read the other way. On top, on a layer of its own with source-atop, the shade of turning edge-on and a sheen running across; the thickness is a band 1.7 · |sin φ| wide. The layer is laid on the door with shadowBlur: the shadow follows the true outline.

The pointer brushing past

The pointer's speed is measured between one pointermove and the next. If it is over the sign (in the sign's own frame, turned by θ), the moment of its push about the nail, (r × v) / |r|², becomes an acceleration of θ, plus a small push on φ. The pointer coming onto the pill gives a push from the side it comes from. When swing, turn and twine are still the loop stops working and drawing.

The states

A real button with aria-disabled while it signs out or in, an aria-live region saying Signing out…, Signed out. See you soon, Ada., Signing in… and Signed in. Welcome back, Ada. The pill is as wide as the longest of the six phrases measured already wide (font-stretch 116%), so it never changes size. The old words leave before the new ones come in; the closed door is a second coat that shows through the --shut variable.

Values to change

NameFileDefaultWhat it changes
SIGN_OUTscript.jsSign outThe words while signed in.
SIGNING_OUTscript.jsSigning out…The words while it signs out; the aria-live region says it too.
SIGNED_OUTscript.jsSee you soon, AdaThe goodbye once signed out. The name is made up: in a real site it is the user's.
SIGN_INscript.jsSign inThe words while signed out, offering the way back.
SIGNING_INscript.jsSigning in…The words while it signs in.
SIGNED_INscript.jsWelcome back, AdaThe welcome once signed in again.
FRONTscript.jsOPENThe word on the sign's front, while signed in. If it is long the type gets smaller.
BACKscript.jsCLOSEDThe word on the back, while signed out.
WAITscript.js1.3The seconds of the simulated sign out or sign in. In a real site, your server's answer.
SAIDscript.js0.9The seconds the goodbye stays before the button offers the way back.
TURNSscript.js1.25How many turns the sign goes round at a click before the twine stops it.
SWINGscript.js1.15The swings a second on the nail: higher a light sign, lower a heavy one.
TWISTscript.js0.72How many times a second the sign turns back and forth on the twisted twine.
DAMPINGscript.js0.3How soon it stops: 0.05 it swings and turns a long time, 1 it stops at once.
BRUSHscript.js1How hard the pointer brushing past sets it swinging: 0 it does not touch it.

Try them in the Playground

When to use it

For signing out of an account, or in, on a site with a handmade character: an online shop, a café, a bookshop, a bakery, a neighbourhood store; also as an Open / Closed switch in the dashboard of someone running a place (bookings open, orders paused). The sign out has to be wired to your own: here it is all simulated.

On phones

The pill is about 270 pixels wide and fits at 390 with room to spare; if the words are longer than the room the type gets smaller. A tap spins the sign; a finger dragged over it sets it swinging.

With reduced motion

The sign hangs still and straight, the twine taut; it neither swings nor turns. At a click the OPEN face fades into the CLOSED face (and back), the door darkens and the words change with a fade; the pill does not swell and the words do not widen (under the mouse the fill only lightens a little).

Pitfalls

  • Drawing the strips side by side at fractional pixels, the soft edges of two neighbouring strips let the background through: light vertical lines all over the sign. Each strip now reaches a pixel under the next.
  • With a push set by eye the sign sometimes stopped on the wrong face, or spun too far when TWIST and DAMPING changed. The click's speed is now worked out by running the spring forward and finding it by bisection.
  • With the loop as wide as the sign, the twine had no length to twist: two eyelets 30 pixels apart on strands 22 long leave at most 7 pixels of braid. The eyelets sit at ±15, so the braid shows and lifts the sign.
  • The pointer's speed was measured with the physics clock, which stops while the sign sleeps: the first pass over a still sign did not move it. It is now measured with performance.now.
  • A pointer rushing across the 60 pixel sign touched it for a few milliseconds and nothing showed. The sensitive area is 10 pixels wider each side and the push stronger, and coming onto the pill gives a push of its own.

Libraries

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0, Fraunces (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 12 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