Skip to content
animorio
IT
All components

The barbell rating

Ratings, Physics, SVG, HoverGSAPAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

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.

The code

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>

Playground

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

Or ask Claude Code for it

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 site

What it does

On a white background the question How hard was today’s workout?, in a narrow black type; below it, a barbell seen from the side and a little from its right: the graphite steel bar with knurling where the hands go, the two fixed collars and the chrome sleeves, and on the sleeves the pale silhouette of five plates a side, the empty places; lower down, in grey, Slide along the bar. Moving the pointer over it, from left to right, loads the barbell: for every step of effort a pair of rubber plates comes in from the ends, slides along the sleeve, knocks against the plate before it with a little bounce and stops; the extra ones slide back, leave the sleeve and drop, turning as they vanish. The plates go from yellow to orange, vermilion, red and oxblood. Under the weight the bar bends like a real one: between the hands it arches up a touch, past the hands the ends droop and the plates tilt with them; every plate that lands sets it swinging before it settles, slower the heavier it is. Below reads the word for that load, Easy, Moderate, Hard, Very hard or All out, which grows wider and bolder as the plates land: narrow and light with one plate, wide and pitch black with five. Leaving, the barbell goes back to the load picked, or empties if there is none yet. On a pick, the collars slide onto the sleeves and their orange lever snaps shut, then the barbell is pulled up and set down again: the ends lag, whip and swing, the more the heavier it is, and with the top load the word trembles for a moment at the peak of the effort. Below appears Logged. Your next plan adapts to it., and at every change of mind Changed to followed by the new load: the collars open, the plates change and the collars shut again. A finger slides along the bar and the choice falls where it lifts; with the keyboard the arrows load at once and log the choice after a short pause.

How it works

A bar over a real range input

Over 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.

A beam that bends

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.

The plates, a spring and a knock

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.

Drawn from far to near

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.

Collars, the lift and the word

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 empty places, fingers and the preview

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.

Values to change

NameFileDefaultWhat it changes
LABELSscript.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).
STARTscript.js0The load picked when the page opens (0 = an empty bar).
BENDscript.js3.6How far the ends of the bar droop for each pair of plates, in pixels.
WHIPscript.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.
LIFTscript.js9How high the barbell is pulled up when the choice is logged, in pixels (a little less with heavy loads).
CLANKscript.js0.3How much a plate bounces off the one before it (0 = not at all, 1 = with all its speed).
STAGGERscript.js0.07The time between one plate and the next as they go on or come off, in seconds.

Try them in the Playground

When to use it

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.

On phones

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.

With reduced motion

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.

Pitfalls

  • The first plates had competition colours (green, yellow, blue, red): side by side they recalled a famous company’s logo. Now they run in a ramp from yellow to oxblood, which also says the effort is growing.
  • Seen a little from the right, the sleeve has to disappear into the plates and come out of the hub; one stacking order cannot do it, because the bar runs through every depth. It is drawn three times: whole, and twice with a use cut by a clipPath that follows the plates at every frame.
  • The highlight on the bar took the knurling’s dashes: a class rule in the CSS beats the SVG’s stroke-dasharray="none" attribute. It has a class of its own.
  • A damped swing never really ends: the ticker kept redrawing sub-pixel wobbles for over six seconds. Under 0.08 pixels the bar stops and the ticker is removed.
  • The collars waited for the plates to be completely still, and the lift started a second after the tap: now they come in once the plates are within 1.2 pixels of their place, and their own slide covers the rest.
  • The plates falling off the ends flew out of the component’s box: their outward push is limited and they fade in a seventh of a second, before they reach the text.
  • The question in Anybody at normal width ran onto two lines: it is set at font-stretch 74%, so it fits on one line even at 390 and the component is as tall as the other ratings.
  • Enter on the still empty bar logged Easy: with no load chosen the confirming keys do nothing.

Libraries

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.

Testing

Tested on 6 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

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.

All components