
The needle gauge
Counters, Physics, SVG
NewFree
The Apply coupon line of a cart, with an orange paper coupon in the pill: the code AUTUMN15, a stub with a leaf and a row of holes between them. At a click the coupon tears along the holes: the ragged edge is worked out anew every time, the white core of the paper shows, and the torn half turns about the point where it still holds, comes free and falls spinning like a card, then fades away under the pill. The coupon can also be pulled by hand. After Checking… a pen strikes €120.00 through, the old price shrinks away upwards and the new one rolls down with its digits spinning on their wheels to €102.00, and a −15% badge pops in. Another click takes the discount off: the stroke is wiped, the digits roll back up and a fresh coupon rises in its place.
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 coupon 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/jetbrains-mono@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the coupon button: the Apply coupon line of a cart, with a small paper coupon in the pill, perforated down its
stub. A click (or a pull on the coupon) tears it along the holes: the torn edge is worked out anew every time,
the torn half falls away spinning, and the price is struck through with a pen and rolls down to the new one.
Another click takes the discount off and a fresh coupon rises in its place -->
<div class="deal" data-deal>
<button class="deal__btn" type="button" data-btn data-autoclic>
<span class="deal__fill" aria-hidden="true"></span>
<svg class="deal__coupon" viewBox="0 0 104 42" aria-hidden="true" data-coupon>
<defs>
<linearGradient id="cp-paper" x1="0" y1="0" x2="0.35" y2="1">
<stop offset="0" class="cp-a"/>
<stop offset="1" class="cp-b"/>
</linearGradient>
<pattern id="cp-print" width="3.2" height="3.2" patternUnits="userSpaceOnUse" patternTransform="rotate(32)">
<path class="cp-print" d="M0 1.6h3.2"/>
</pattern>
<clipPath id="cp-cut-stub"><path data-cut="stub"/></clipPath>
<clipPath id="cp-cut-voucher"><path data-cut="voucher"/></clipPath>
<g id="cp-art">
<!-- the paper: a ticket with a notch top and bottom where the perforation runs -->
<path class="cp-paper" d="M3.5 0H18.6A3.4 3.4 0 0 0 25.4 0H100.5A3.5 3.5 0 0 1 104 3.5V38.5A3.5 3.5 0 0 1 100.5 42H25.4A3.4 3.4 0 0 0 18.6 42H3.5A3.5 3.5 0 0 1 0 38.5V3.5A3.5 3.5 0 0 1 3.5 0Z"/>
<path class="cp-printed" d="M3.5 0H18.6A3.4 3.4 0 0 0 25.4 0H100.5A3.5 3.5 0 0 1 104 3.5V38.5A3.5 3.5 0 0 1 100.5 42H25.4A3.4 3.4 0 0 0 18.6 42H3.5A3.5 3.5 0 0 1 0 38.5V3.5A3.5 3.5 0 0 1 3.5 0Z"/>
<rect class="cp-dash" x="28.5" y="4.5" width="71" height="33" rx="2.4"/>
<!-- a fallen leaf on the stub -->
<g class="cp-leaf" transform="translate(11 21) rotate(-28)">
<path d="M0 -8.2C3.6 -5.6 4.6 -1.6 3.4 2.2C2.6 4.6 1.2 6.2 0 7C-1.2 6.2 -2.6 4.6 -3.4 2.2C-4.6 -1.6 -3.6 -5.6 0 -8.2Z"/>
<path class="cp-vein" d="M0 -6.6V9.6M0 -2.6L2.4 -4.4M0 -2.6L-2.4 -4.4M0 0.9L2.8 -0.9M0 0.9L-2.8 -0.9M0 4.1L2.2 2.6M0 4.1L-2.2 2.6"/>
</g>
<text class="cp-off" x="34" y="16.2" data-off-text>15% OFF</text>
<text class="cp-code" x="33.6" y="30.4" data-code-text>AUTUMN15</text>
</g>
</defs>
<g class="cp-piece" data-piece="stub">
<g clip-path="url(#cp-cut-stub)"><use href="#cp-art"/></g>
<path class="cp-fiber" data-fiber="stub"/>
</g>
<g class="cp-piece" data-piece="voucher">
<g clip-path="url(#cp-cut-voucher)"><use href="#cp-art"/></g>
<path class="cp-fiber" data-fiber="voucher"/>
</g>
</svg>
<span class="deal__faces" data-faces>
<span class="deal__face" data-face="rest">
<span class="deal__word" data-wide="Apply coupon" data-rest-text>Apply coupon</span>
</span>
<span class="deal__face deal__face--busy" data-face="busy" aria-hidden="true">
<span class="deal__shine" data-busy-text>Checking…</span>
</span>
<span class="deal__face deal__face--done" data-face="done" aria-hidden="true">
<span class="deal__lines">
<span class="deal__applied" data-applied-text>Coupon applied</span>
<span class="deal__sub"><span class="deal__code" data-code-small>AUTUMN15</span> · <span data-remove-text>Remove</span></span>
</span>
</span>
</span>
<span class="deal__chip" aria-hidden="true" data-chip>−15%</span>
<span class="deal__price" data-price>
<span class="deal__was" aria-hidden="true" data-was>
<span data-was-text>€120.00</span>
<svg class="deal__strike" aria-hidden="true" data-strike-svg><path data-strike/></svg>
</span>
<span class="deal__now" data-now></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.
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 coupon button” from Animorio and fit it into my siteThe coupon is an SVG group in defs (the paper with its gradient, a fine print of slanted lines, the dashed frame, the leaf on the stub, 15% OFF and the code in JetBrains Mono) used twice with use: one copy is the stub, the other the voucher, and each has its own clipPath. The two clipPaths meet along the tear line, so at rest the coupon looks whole; the perforation's holes are not drawn, they are the gaps left outside both cuts, and the pill shows through them.
For every new coupon a random generator with a new seed builds the tear: between one hole and the next (every 3.1 units, radius 0.72) the little bridge of paper gives way along three or four points pushed sideways at random, most in the middle of the bridge (a sine), as much as RAGGED says; at every hole the stub keeps its left half-circle and the voucher its right one. So the two edges fit each other, and the edge is never the same twice. Along the part already torn an ivory line is drawn, the white core of the paper under the print.
While the tear runs down (tear.t from 0 to 1 in TEAR seconds, or following the hand) the voucher turns about the point where it still holds, up to 11 degrees. At the bottom it becomes a free card: its centre and angle come from the turn about the last point, its speed from a small push (plus the hand's, if it was pulled), and from there gravity (GRAVITY), air drag stronger across than down, a sideways push following sin 2a like a card dropped flat, and a spin that slows (SPIN). Steps of 1/240 of a second, the frame capped at 1/30; after twenty pixels of fall the voucher fades in eighty. The stub gets a knock on a spring and shakes.
The pen stroke is a new path for every discount (a little uphill, bowed, with a hook where the pen lifts), drawn with stroke-dashoffset. The price is made of wheels: every digit that changes is a strip with a blank and three rounds of figures behind a window one figure high, with a mask fading it at the top and bottom; the full price uses the third round and the cut price the second, so going down every wheel turns downwards and coming back it turns up. The old price is set in the same equal-width cells, so it sits exactly over the wheels when the pen strikes it, and then shrinks away upwards.
The coupon can also be pulled: pointerdown on the voucher (not on the stub) captures the pointer, and moving right or down becomes the tear, (dx · 0.7 + dy · 0.7) / 70. Let go past halfway it rips the rest of the way and the hand's speed goes into the fall; before that it springs back into place. After a drag the click that follows is ignored. The coupon has touch-action: none, so on a phone the finger pulls instead of scrolling the page; a plain tap stays a click.
The button is a real button with an aria-label that says it all (Apply coupon AUTUMN15. Total €120.00; once applied the code, the discount, the new total and the old one, and Remove coupon), an aria-live region (Applying AUTUMN15…, AUTUMN15 applied: 15% off. New total €102.00., Coupon removed.) and aria-disabled while it works. Checking the code is simulated. Applying takes about three seconds, more than the 2.8 between two clicks of the preview: the second click falls in the wait and the discount stays in view for a whole round.
| Name | File | Default | What it changes |
|---|---|---|---|
LABEL | script.js | Apply coupon | The words at rest. If they are too long for the room, the type gets smaller. |
CODE | script.js | AUTUMN15 | The code printed on the coupon, repeated under Coupon applied and in the aria-live region. |
PERCENT | script.js | 15 | The discount in percent: it changes the coupon (15% OFF), the badge and the new price. |
PRICE | script.js | 120 | The cart total before the discount; the cut one is worked out. |
CURRENCY | script.js | € | The symbol written before the price. |
BUSY | script.js | Checking… | The words while the code is checked (simulated). |
APPLIED | script.js | Coupon applied | The words once the discount is on. |
REMOVE | script.js | Remove | The small word that says another click takes the discount off. |
TEAR | script.js | 0.34 | The seconds the tear takes to run down the perforation. |
RAGGED | script.js | 1 | How rough the torn edge is: 0 clean from hole to hole, 2 very rough. |
GRAVITY | script.js | 1500 | How hard the torn half falls, in pixels per second squared. |
SPIN | script.js | 1 | How much the torn half spins as it falls: 0 it drops straight. |
ROLL | script.js | 0.9 | The seconds the digits spin to the new price. |
For the discount code of a cart or a checkout: a seasonal voucher, a welcome code, an influencer's code, a gift card. It suits fashion, home and cosmetics shops and any seasonal promotion. Checking the code is simulated: in a real shop the click sends the code to the server, and the tear can start at once while the pen waits for the answer.
The pill at rest is about 345 pixels wide and the discounted one about 320: at 390 pixels they fit, and if the words are longer than the room the type gets smaller. A tap tears the coupon; a finger can also pull it, and over the coupon the page does not scroll. The falling voucher leaves under the pill and fades before it reaches the bottom of the screen.
No tear running down, no fall and no spinning wheels: at a click the voucher fades away leaving the stub with its torn edge, the pill takes its new width while the words are gone, and the struck price, the new price and the badge fade in. The coupon does not tug and the pill does not swell (under the mouse the fill only lightens a little). Another click fades it all away and a fresh coupon fades in.
GSAP 3.15.0, Mona Sans (font variabile) 5.3.0, JetBrains Mono (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 14 KB; the libraries: GSAP 28 KB.
Tested on 10 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.