
The slide-to-confirm button
Buttons, Physics, Transitions
NewFree
How sweet a cake should be, chosen on a ball of soft toffee: pulled away from a dollop of caramel resting on baking paper, it draws a thread behind it that stretches, thins, bows under its weight and shines. Pushed back, the slack thread droops and draws itself in slowly, like warm caramel. The colour goes from a bitter burnt brown to blond, and the word above gets fatter and softer with the sweetness.
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 toffee sweetness slider</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">
<!-- how sweet a patisserie makes your cake, chosen on a ball of soft toffee. A dollop of caramel sits at the
start of a strip of baking paper; the knob is a glossy toffee ball pulled out of it, and the slider's fill is
the thread of caramel that stretches between the two. The paper, the dollop, the thread and the ball are drawn
by the script on the canvas; the real range input underneath carries the value, the keys and the screen
reader -->
<div class="sweet" data-sweet>
<div class="sweet__head">
<label class="sweet__label" for="sweet-range">Sweetness</label>
<span class="sweet__note" aria-hidden="true"><span class="sweet__note-text" data-note>Our house recipe</span></span>
</div>
<p class="sweet__word" aria-hidden="true" data-word>Balanced</p>
<div class="sweet__track" data-track data-autoclic>
<canvas class="sweet__canvas" aria-hidden="true" data-canvas></canvas>
<input class="sweet__range" type="range" id="sweet-range" name="sweetness" min="1" max="5" step="1" value="3" data-range>
</div>
<div class="sweet__scale" aria-hidden="true">
<span data-lo>Bitter</span>
<span data-hi>Very sweet</span>
</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.
38 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 toffee sweetness slider” from Animorio and fit it into my siteThe thread is a chain of 28 weights under Verlet integration: the first is fixed on the dollop, the last is tied to the ball. Every two neighbouring weights are held by a distance constraint, stiff against stretching and almost loose against squeezing, so a slack thread bends and falls instead of pushing. The viscosity pulls each weight’s speed towards its neighbours’, so the waves are slow and thick.
Each piece’s rest length is not fixed: it flows towards the length the thread wants (the distance from the dollop to the ball with the little extra it needs to hang) in FLOW seconds, and a piece stretched past the elastic limit of STRETCH yields and grows longer too. It is a Maxwell spring with a threshold: the thread’s own weight is not enough to make it run. The extra thread of a push back is taken in mostly by the ball and the dollop, so the thread droops, then draws itself in.
The thickness comes from the caramel there is: the longer the thread the thinner it is (between THICK[0] and THICK[1]), and where a piece is stretched past its rest length it necks, where it is squeezed it swells. After a quick pull the beads sit on the caramel itself and travel with it; at both ends the thread widens into a fillet that runs into the dollop and the ball.
Every frame the chain goes through a Catmull-Rom curve and is drawn in four passes: the dark edge, the body, the light inside the caramel and the shine, each in the colour of its thickness (thin caramel lets more light through and looks blonder). The shine is strongest where the thread faces the lamp, so glints run along the thread as it sways. The ball has the gradients of a sphere, the light passing through it, the edge in shade, the reflected window and a swirl of cream worked out on a real sphere and turned as far as it has rolled.
The word moves on Fraunces’ weight and SOFT axes with the caramel, and from Sweet on it switches on WONK, which tips the letters a little. When it changes, each letter of the old one squashes down to the line, spreading, and the letters of the new one ooze up out of it on an elastic spring; if a change comes while the last is still on its way the old drip is skipped, so there are never two words at once.
Under the drawing there is a real, invisible range input: it carries the value, the arrows, Page Up and Down, Home and End and what the screen reader reads (the word and the note). The focus ring shows only when it is reached with the keys. The physics runs on GSAP’s ticker in steps of 1/240 of a second and stops when everything is still. A click from a script (detail 0, the preview’s) is a pretend hand: it pulls the ball all the way out, back to Bitter and then where it was.
| Name | File | Default | What it changes |
|---|---|---|---|
START_AT | script.js | 3 | The sweetness when the page opens, from 1 (the first word) to the last. |
WORDS | script.js | ['Bitter', 'Lightly sweet', 'Balanced', 'Sweet', 'Very sweet'] | The sweetness words, from the first mark to the last: as many words, as many marks. |
NOTES | script.js | ['Dark cocoa, no glaze', 'A spoon of wildflower honey', 'Our house recipe', 'Salted caramel drizzle', 'Double caramel and fudge'] | The note at the top right for each sweetness: what that cake tastes of. |
BALL | script.js | 20 | The radius of the toffee ball, in pixels. |
THICK | script.js | [5.2, 2.3] | Half the thread’s thickness when it is shortest and when it is longest, in pixels. |
SAG | script.js | 1 | How heavy the thread is: higher, and it bows lower when still and falls faster when slack. |
FLOW | script.js | 0.42 | How many seconds the caramel takes to flow to its new length: higher, and it stays taut and thin longer after a pull and draws back more slowly. |
STRETCH | script.js | 0.06 | How far the caramel stretches before it starts to flow, as a share of its length. |
BEADS | script.js | 1 | How strongly a quick pull breaks the thread into beads (0 never). |
SPRING | script.js | [380, 0.42] | The spring that takes the ball to its mark when nobody holds it: how stiff, and how much it is held back. |
For the sweetness of a cake to order at a patisserie or in a delivery app, the sugar in coffee or tea, how dark the caramel should be in a sweet shop, or any choice on a soft scale where the value should make your mouth water.
The slider takes the width of the screen minus a margin and the ball reaches the end of the paper; the big word drops to 44 pixels. A finger drags it sideways while the page still scrolls up and down (touch-action: pan-y); a tap on the paper sends the ball there, running and then following the finger. Physics, beads and reflections are the same.
No physics, no entrance, no rolling or wobbling: the ball is at once on its mark and the thread already hangs still, as thick as its length says. The caramel colour still changes, and the word and the note change with a fade.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 15 KB; the libraries: GSAP 28 KB.
Tested on 17 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.