
The needle gauge
Counters, Physics, SVG
NewFree
A hotel's room service button on a black background: in the round end of the night-blue pill a silver cloche on its plate, mirroring a dark room. Under the pointer a hand lifts the cover a crack and a thread of steam slips out; at a click the cover rattles on the plate like a pot lid while the order goes, then lifts and tilts back, the steam rolls up over the croissant and the pill says On its way. A moment later the cover comes down with a clink and settles.
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 cloche button</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the cloche button: the room service button of a hotel, with a silver cloche on its plate in the round left end
of the pill. Under the pointer the cover lifts a crack and a breath of steam slips out; a click rings the
kitchen and the cover rattles on its plate while the order goes; then a hand lifts it, the steam rolls out
over the breakfast and the pill says it is on its way. A moment later the cover comes down with a clink -->
<div class="cloche" data-cloche>
<div class="cloche__unit">
<button class="cloche__btn" type="button" data-btn data-autoclic>
<span class="cloche__fill" aria-hidden="true"></span>
<span class="cloche__faces">
<span class="cloche__face" data-face="rest">
<span class="cloche__word" data-rest-text>Order breakfast</span>
<span class="cloche__price" data-rest-price>€24</span>
</span>
<span class="cloche__face cloche__face--two" data-face="busy" aria-hidden="true">
<span class="cloche__small" data-busy-small>Room 214 · Continental</span>
<span class="cloche__big" data-busy-text>Calling the kitchen</span>
</span>
<span class="cloche__face cloche__face--two" data-face="done" aria-hidden="true">
<span class="cloche__big" data-done-text>On its way</span>
<span class="cloche__small" data-done-small>At your door in 25 min</span>
</span>
</span>
</button>
<!-- the plate, the breakfast and the cover, and above them the steam; the pointer goes through both -->
<canvas class="cloche__steam" aria-hidden="true" data-steam></canvas>
<svg class="cloche__art" viewBox="-16 -60 96 124" aria-hidden="true" data-art></svg>
</div>
<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.
26 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 cloche button” from Animorio and fit it into my siteA single SVG lies over the end of the pill, in the pill's coordinates (pointer-events: none, so the click reaches the button). The dome is a path with a horizontal silver gradient and, clipped inside it with a clipPath, a second vertical gradient that is the room it mirrors: light at the top, a dark band for the horizon, the plate's warmth at its foot. Over it, two glints softened with a feGaussianBlur. The gradients take their colours with stop-color: var(--silver) and the like, so the Playground's swatches change them. Every frame only the transform of the cover's group changes.
The cover has no single height: its two rims, left and right, each have a height and a speed and a spring pulling them where the hand wants them (at rest 0, under the pointer 7.5 and 0.8 pixels, open LIFT + 9 and LIFT − 9). The plate stops them from below: a rim that hits it bounces back with 34% of its speed. The average height is how high the cover is, the difference gives its angle (atan2 of the difference over its width). A constraint keeps the difference within what a rigid cover can do.
While the order goes, every 0.1–0.23 seconds a random rim gets a kick upwards (34–74 pixels a second times RATTLE), sometimes the other one a little too: the spring brings it down, the plate stops it and it bounces, and the two rims together make the rattle of a lid on a pot. Each time a rim is up, the gap lets out steam; each hard knock on the plate jolts the pill's fill.
The steam is puffs on a canvas: each has a position, a speed, an age and a lifetime, and every step its speed chases a wind that is the curl of a Perlin noise drifting in time, plus a lift that grows with age. The curl of a smooth field has no sources or sinks, so the puffs swirl instead of bunching up. The dish breathes from three spots that wander slowly: puffs born at the same spot follow the same wind and draw threads instead of a fog. Each puff is a soft disc drawn once and stamped, stretched along its way.
Three faces on top of each other, as wide as their words (at rest, ordering, on its way); the pill is measured once on the widest. The button is a real button with aria-disabled while it orders and while the cover is up, an aria-live region that says Calling the kitchen… and then On its way. At your door in 25 min., and an aria-label that changes with the state. GSAP's loop works only while something moves, and stops off screen (IntersectionObserver).
| Name | File | Default | What it changes |
|---|---|---|---|
LABEL | script.js | Order breakfast | The words at rest. |
PRICE | script.js | €24 | The price beside the words. |
ORDER | script.js | Room 214 · Continental | The small line while the order goes: the room and what was ordered. |
CALLING | script.js | Calling the kitchen | The words while the cover rattles; the aria-live region says them too. |
DONE | script.js | On its way | The words once the cover is lifted. |
ETA | script.js | At your door in 25 min | The small line under them, with the delivery time. |
WAIT | script.js | 1.6 | The seconds the order takes: the cover rattles all the while. In a real site it is the time the server takes to answer. |
HOLD | script.js | 2.6 | The seconds the cover stays up before it comes down. |
LIFT | script.js | 34 | How high the hand lifts the cover, in pixels. |
STEAM | script.js | 1 | How much steam: 0 none, 2 a cloud. |
RATTLE | script.js | 1 | How hard the cover rattles while the order goes: 0 it waits still, 3 it jumps hard. |
For a hotel's or a holiday home's room service, for ordering at the table in a restaurant, for a kitchen's or a bakery's delivery, for the Order now of an online menu. The button fakes the order: in a real site the click calls the kitchen, the cover rattles until the server answers and lifts when the order is confirmed.
The pill is about 320 pixels wide and the steam rises over its left end: at 390 pixels it fits. A tap makes the cover jump, squashes the fill and orders. The cloche keeps its size.
The cover does not move and there is no steam: when the order is confirmed the cover fades away and the croissant shows, then it fades back. The words fade in, the pill does not swell and the words do not change shape (under the mouse the fill only lightens a little).
GSAP 3.15.0, Fraunces (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 12 KB; the libraries: GSAP 28 KB.
Tested on 19 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.