
The water button
Buttons, SVG, Physics, Hover
NewFree
Effort at the end of a workout is rated by loading a barbell: sliding along the bar, a pair of rubber plates for every step slides onto the sleeves and knocks against the one before, and the bar bends under the weight like a real beam. Once the load is picked, the collars snap shut, the bar is pulled up and its ends whip, slower the heavier it is; the word underneath grows wider and bolder with the load.
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 barbell rating</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/anybody@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the effort question at the end of a workout, answered by loading a barbell. Sliding along the bar loads it:
a pair of rubber plates for every step of effort slides onto the sleeves, knocks against the plate before it,
and the bar bends under the weight. Picked, the lock collars snap shut and the bar is pulled up, whipping at
the ends; the word under it grows wider and bolder with the load. Under the bar there is a real range input;
the bar itself is drawn by the script -->
<form class="lift" data-lift onsubmit="return false">
<p class="lift__ask" id="lift-ask">How hard was today’s workout?</p>
<div class="lift__bar" data-bar data-autoclic>
<svg class="lift__art" aria-hidden="true" data-art>
<defs data-defs></defs>
</svg>
<input class="lift__range" type="range" name="effort" min="0" max="5" step="1" value="0" aria-labelledby="lift-ask" aria-valuetext="Not rated yet" data-range>
<span class="lift__ring" aria-hidden="true"></span>
</div>
<p class="lift__word" aria-hidden="true"><span data-word>Slide along the bar</span></p>
<p class="lift__thanks" role="status" aria-live="polite" data-thanks></p>
</form>
</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.
30 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 barbell rating” from Animorio and fit it into my siteOver the barbell lies a real range input, see-through and with no pointer events: Tab goes into it, the arrows, Home and End change the load, screen readers read the question (aria-labelledby) and the load’s word (aria-valuetext), and the form sends the value. Its minimum is 0 (not picked yet) until the first pick, then 1. The hand works on the bar through pointer events; the keys load at once and log the choice after a 0.55 second pause (a gsap.delayedCall that every key restarts), Enter and Space log it straight away.
The shape of the bend comes from beam theory: a bar held at GRIP from the middle with its weight past the hands. Between the hands the curvature is constant and the bar arches up; past them, the turn at the support adds to the deflection of a cantilever. All of it comes down to one number, how far the tip droops, which goes towards BEND times the load on a spring: ω²(qs − q) − 2ζω·q′ minus the middle’s acceleration times the share of the mass that is plates. Each plate weighs by its arm (its distance from the hands) and only once its hole is on the sleeve; ω drops with the load (WHIP[0] is the frequency with a full load) and ζ is WHIP[1]. So when the hands pull the bar up, the ends lag behind and whip.
Each plate is a body sliding along the sleeve, pushed towards its place by a lightly damped spring, so it arrives with speed. It cannot go into the plate inside it (or the fixed collar): its position is corrected and its speed bounces back by CLANK, so it knocks and stops. Plates go on from the innermost, STAGGER seconds apart, and come off from the outermost; past the end of the sleeve a plate drops, with gravity, a turn and a fade. The sums run on GSAP’s ticker in steps of 1/240 of a second, with the frame capped at 1/30; once nothing moves the ticker is removed, and a swing under 0.08 pixels stops.
The barbell is seen a little from its right: a plate is a short cylinder, the rim between two half ellipses and the face (only the one towards the right) with its ring and steel hub. It is drawn from far to near, that is from left to right. The bar, though, is one object running through everything: it is drawn whole, then twice more with a use cut by a clipPath that moves at every frame, from the inner face of the innermost plate on the left onwards and from the outer face of the last plate on the right onwards, so the sleeve disappears into the plates and comes out of the hub. The chrome’s lights and shadows are lines parallel to the bend, shifted along its normal; the knurling is a dashed stroke that follows the curve.
On a pick, once the plates are nearly still, the collars slide onto the sleeves on a GSAP timeline and the lever shuts with back.out; then the hands, a value taken up and down by another timeline, pull the middle of the bar through a stiff spring: more load means a slower, lower pull and a longer pause at the top. The word is set in Anybody: font-stretch from 80 to 142% and weight from 420 to 890 follow the load on the bar, a little behind, so the word swells with every plate that lands; with the top load it trembles by 0.8 pixels at the peak of the effort.
The pale silhouette of the ten places sits behind the bar while choosing and fades once the collars shut. A finger slides sideways along the bar (touch-action: pan-y) and the choice falls where it lifts; a mouse button let go away from the bar picks nothing. A click from a script (detail 0, the library’s preview) is a pretend hand: it runs to the load and logs in turn All out, Moderate, Very hard, Easy and Hard.
| Name | File | Default | What it changes |
|---|---|---|---|
LABELS | script.js | ['Easy', 'Moderate', 'Hard', 'Very hard', 'All out'] | The word for each load, from the lightest to the heaviest (and how many plates a side: 2 to 8; the colours share out the ramp from yellow to oxblood). |
START | script.js | 0 | The load picked when the page opens (0 = an empty bar). |
BEND | script.js | 3.6 | How far the ends of the bar droop for each pair of plates, in pixels. |
WHIP | script.js | [2.4, 0.085] | The bar’s whip with a full load: how many times a second it swings and how quickly it calms down. |
LIFT | script.js | 9 | How high the barbell is pulled up when the choice is logged, in pixels (a little less with heavy loads). |
CLANK | script.js | 0.3 | How much a plate bounces off the one before it (0 = not at all, 1 = with all its speed). |
STAGGER | script.js | 0.07 | The time between one plate and the next as they go on or come off, in seconds. |
For perceived effort at the end of a workout in a gym, running or cycling app (the RPE scale), for the log a coach reads, to ask how hard a fitness class or a climb was: wherever a rating means weight and effort, and stars would rather say whether you liked it.
The barbell narrows with the screen: 358 pixels at 390. A finger slides sideways along the bar and the choice falls where it lifts; up and down the page still scrolls (touch-action: pan-y). A tap picks straight away.
No sliding, no swinging and no lift: the plates fade in and out in their places, the bar takes its bend at once and the collars fade in; the word changes in a fade and takes its width at once, without trembling.
GSAP 3.15.0, Anybody (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 12 KB; the libraries: GSAP 28 KB.
Tested on 6 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.