
The copy button
Buttons, Text, SVG, Physics
NewFree
The Do not disturb switch of a focus app, in the minimal style: the track is a hairline of wire stretched between two posts, and the knob is a bead threaded on it. The bead’s weight bends the wire; turned on, the bead runs to the far end, goes a pixel past and back, and the wire rings and settles. Beside it the word Off thins out and fades, On comes in thin and takes weight.
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 taut wire switch</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 Do not disturb switch of a focus app: the track is a hairline of wire stretched between two posts, and the
knob is a bead threaded on it. The bead's weight bends the wire; sliding along, it drags the bend with it, and
when it lands the wire rings and settles. The word next to it takes weight as the bead travels -->
<div class="dnd" data-dnd>
<label class="dnd__name" id="dnd-name" for="dnd-switch" data-name>Do not disturb</label>
<div class="dnd__row">
<button class="wire" id="dnd-switch" type="button" role="switch" aria-checked="false" aria-labelledby="dnd-name" data-switch data-autoclic>
<svg class="wire__art" aria-hidden="true" focusable="false" data-art>
<line class="wire__post" data-post></line>
<line class="wire__post" data-post></line>
<path class="wire__line" data-line></path>
<g class="wire__bead" data-bead>
<circle class="wire__ball" data-ball></circle>
<circle class="wire__ball wire__ball--on" data-ball-on></circle>
</g>
</svg>
</button>
<label class="dnd__state" for="dnd-switch" aria-hidden="true" data-state>
<span class="dnd__word dnd__word--off" data-off>Off</span>
<span class="dnd__word dnd__word--on" data-on>On</span>
</label>
</div>
</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.
20 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 taut wire switch” from Animorio and fit it into my siteIt is a button with role switch and aria-checked, named by the words above it (aria-labelledby): Space and Enter turn it on and off, the right and left arrows set it on or off, and the name and the word beside it are labels for it, so they can be clicked. Inside there is a single SVG with the two posts, the wire (a path) and the bead, a group of two circles: white below and orange above, with an opacity that follows the bead along the wire.
The wire’s displacement is the sum of its first 48 modes, sin(n π x / L), each a damped oscillator driven by the bead’s weight at the point where it is. Still, the modes add up to exactly the V of a string under a point load, deepest in the middle (4 SAG a(L − a) / L²): that is why the bead, crossing, rides down a curve. The wire’s notes are n times TONE, a touch sharp as in a slightly stiff wire, and the higher ones die sooner.
Every mode moves on with the exact solution of its equation over the step (1/240 of a second), not with Euler: the high notes, far faster than the screen, stay stable at any frame rate and follow the bead, the low ones ring. The sines of the 48 modes at the bead are worked out with the recurrence sin((n+1)θ) = 2 cos θ sin(nθ) − sin((n−1)θ); those of the 84 points of the drawing are in a table rebuilt on resize. In the path the bead’s point is put in exactly, so the bend is sharp.
Along the wire the bead is a mass on a spring (SPRING) damped just under critical (DAMPING 0.82): over a hundred pixels of travel it goes about a pixel past. When it crosses its place it plucks the wire: every mode takes a kick of speed in proportion to sin(nθ) / n^1.5 and to the speed of arrival (RING), and the word takes a knock of weight on a soft spring of its own. Pressed, its weight becomes PRESS times; dragged, the weight is worked out to hold it at the height of the finger, and let go it goes back to 1: the wire springs back by itself, because the modes stayed where they were.
Off and On are two words in the same place, in variable Archivo, and their axes follow the bead: Off goes from weight 320 to 120 and from width 100 to 70 as it fades away upwards, On from 130 to 760 and from width 70 to 100 as it comes in from below; to On’s weight the knock of the landing is added, which goes past and settles. Only what changes is written, and when wire, bead and word are still the loop takes itself off GSAP’s ticker.
| Name | File | Default | What it changes |
|---|---|---|---|
TONE | script.js | 4.2 | The wire’s lowest note, in vibrations a second: higher, a tauter wire. |
DECAY | script.js | 0.04 | How soon the wire stops ringing (the damping of its lowest note). |
SAG | script.js | 5.5 | How far the bead’s weight bends the wire in the middle, in pixels. |
SPRING | script.js | 160 | How hard the spring pulls the bead to its end of the wire. |
DAMPING | script.js | 0.82 | How the bead stops: under 1 it goes a little past (0.82 is about a pixel). |
RING | script.js | 5 | How hard the bead plucks the wire as it lands, in pixels. |
PRESS | script.js | 1.9 | How much heavier the bead gets under a finger or the mouse. |
PULL | script.js | 13 | How far the bead can be pulled off the line, up or down, in pixels. |
PEEK | script.js | 5 | How many pixels the bead leans towards the other end under the mouse. |
WEIGHT | script.js | [320, 760] | The weight of the word Off and of the word On (100 to 900). |
For a switch that asks for calm: Do not disturb, a focus mode, pausing notifications, the silent mode of an app for meditating, reading or writing. The wire settling says by itself that everything has gone quiet. It is a piece to set on its own, on an empty background, where it is noticed; in a long list of settings it is best used for one entry only.
Same size as on a computer, 184 by 56 pixels, and at 390 pixels everything fits with plenty of space around it; under 300 pixels the wire shortens to 156. A tap turns it on and off; as soon as the finger rests on it the bead gets heavier and sinks, as with the mouse. A dragged finger takes the bead along the wire and up and down: that is why the page does not scroll on the switch (touch-action: none), but it does around it. No leaning under the mouse, which a finger does not have.
No springs and no ringing: the wire is drawn still with its bend, the bead fades out at one end and back in at the other already in its colour, and Off and On swap in a fade of three tenths of a second, each with its own weight. As the page opens everything fades in already in place. Pressing does not sink it and it cannot be dragged; clicks, taps and keys work as always.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 8.6 KB; the libraries: GSAP 28 KB.
Tested on 16 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.