
The copy button
Buttons, Text, SVG, Physics
NewFree
In the minimal style: the Send invite of a team app, a thin ink pill on white with a hairline coil spring inside. Hold it down and the spring loads: the words slide right, narrow and get heavier, the coil closes up and turns cobalt blue. Let go and the spring fires the words out of the pill, and Invite sent lands while the spring swings 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 spring-loaded button</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the spring-loaded button: the Send invite of a team app, a thin ink pill on white with a hairline coil spring
inside, between the words and the right end. Press and hold to load it: the words slide right, narrow and
get heavier, and the coil closes up. Let go and the spring fires the words out of the left end; Invite sent
rides in on the rest of the swing and settles. A short click loads and fires it by itself -->
<div class="coil" data-coil>
<button class="coil__btn" type="button" data-btn data-autoclic>
<span class="coil__track" aria-hidden="true">
<span class="coil__word" data-word="a"></span>
<span class="coil__word" data-word="b"></span>
<svg class="coil__spring" data-spring>
<path class="coil__wire" data-wire></path>
<path class="coil__plate" data-plate></path>
<path class="coil__ticks" data-ticks></path>
<path class="coil__lit" data-lit></path>
</svg>
</span>
<span class="sr" data-label></span>
</button>
<p class="coil__note"><svg class="coil__check" viewBox="0 0 14 14" aria-hidden="true"><path d="M2.5 7.4 5.6 10.3 11.5 3.8" pathLength="1" data-check></path></svg><span data-note></span></p>
<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.
16 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 spring-loaded button” from Animorio and fit it into my siteEverything comes from the plate’s distance from its place, x: positive when the spring is loaded, negative when it swings past. Every frame it redraws the coil (a zigzag of TURNS turns between the plate and the end), the plate, the lit ticks, the colour going from ink to cobalt, the outline’s thickness, and the width, weight and place of the words, glued to the plate.
While the finger holds, the plate is pulled towards a target that grows as one minus e to the minus time, up to TRAVEL pixels in about LOAD seconds, by a stiff, heavily damped spring that follows without bouncing. Fully loaded, a tremble of four tenths of a pixel is added.
On release the plate becomes a damped spring around zero (STIFF, DAMP), with the time step capped at 1/30 s: it goes past its place and back. If it was loaded at least 30 per cent, on the first pass through zero the words come away with the plate’s speed, speed up and fade out leaving on the left, and the new ones drop onto the plate with a GSAP tween.
The width goes from 100 to 100 minus SQUEEZE with the load and up to 122 when the spring swings past zero; the weight goes from 420 to 660. The words are glued to the plate by their right edge, so as they narrow they pull away from the left end as if squeezed.
pointerdown loads and captures the pointer, pointerup fires; a press shorter than 200 milliseconds carries on loading by itself and then fires. A click with no pointer (keyboard, preview: detail equal to zero) loads and fires by itself. When nothing moves the loop leaves the GSAP ticker.
| Name | File | Default | What it changes |
|---|---|---|---|
LABEL | script.js | Send invite | The words at rest. |
DONE | script.js | Invite sent | The words once the invite has gone. |
NOTE | script.js | To ana@northfield.studio | The small line under the pill. |
NOTE_DONE | script.js | Ana will get it in a moment | The small line once the invite has gone. |
LOAD | script.js | 0.9 | Seconds of holding to load the spring all the way. |
TRAVEL | script.js | 42 | How far the loaded spring closes, in pixels. |
STIFF | script.js | 260 | The stiffness of the spring: higher fires faster. |
DAMP | script.js | 9 | How quickly the swing dies away after firing. |
TURNS | script.js | 9 | The turns of the coil. |
SQUEEZE | script.js | 30 | How much the words narrow when the spring is loaded. |
For an action that goes and does not come back, worth feeling: sending an invite or a message, publishing, launching a sign-up. Holding avoids sending by mistake, and the short click stays for people in a hurry.
The pill narrows to 32 pixels from the edges and the spring is worked out again on the real width. A holding finger loads it like the mouse, and the pill does not scroll the page while held; a short tap loads and fires by itself. The spring closing on hover is there only with a mouse.
No moving spring and no flying words: on click the words swap with a fade, the line underneath changes and the tick appears.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 6.1 KB; the libraries: GSAP 28 KB.
Tested on 23 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.