Skip to content
animorio
IT
Back to the library

The word lifted for reps

Text, PhysicsGSAPMediumCompressed weight: Demo 5.5 KBGSAP 28 KBFreeDownload the zip

A minimal-style text animation for an invented gym: a word stands on a thin bar resting on its hooks, like a barbell. On every rep the bar goes up, bends with a real spring, lands one pixel low and the word gains weight; when the set is done the bar turns vermilion. A click adds one more rep.

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 word lifted for reps</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" data-stage role="button" tabindex="0" aria-label="Kettle gym. The word is lifted like a barbell, one rep at a time. Click for one more rep.">
    <p class="brand" aria-hidden="true">Kettle gym</p>
    <p class="plan" aria-hidden="true">Morning set</p>

    <!-- the rack, the bar, the plates and the word: one SVG, drawn by the script -->
    <svg class="scene" data-scene aria-hidden="true">
      <path class="floor" data-floor d="" />
      <path class="upright" data-upright-l d="" />
      <path class="upright" data-upright-r d="" />
      <g data-load>
        <path class="bar" data-bar d="" />
        <g data-plates></g>
        <text class="word" data-word text-anchor="middle"></text>
      </g>
    </svg>

    <div class="count" aria-hidden="true">
      <span class="count__label">Reps</span>
      <span class="count__num" data-num>0</span>
      <span class="count__ticks" data-ticks></span>
      <span class="count__done" data-done>Set done</span>
    </div>

    <p class="hint" aria-hidden="true">Tap for one more rep</p>
    <h1 class="sr-only" data-title>LIFT</h1>
  </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.

18 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 word lifted for reps” from Animorio and fit it into my site

What it does

A warm white page with, small at the top, ‘Kettle gym’. In the middle a thin bar rests on two hooks, like a barbell on its rack, with two outlined plates on each side, and standing on the bar is the word LIFT, thin and wide. Below, a counter: ‘Reps’, the number and eight grey ticks. Then the set starts. The word squashes slightly, like someone taking a breath, and the bar leaves the hooks and rises: the ends with the plates lag behind the middle and the bar bends like a real one, then straightens and swings a little as it reaches the top. On the way up the word gets darker and a bit narrower, the number goes up and a tick fills with ink, growing from the left. The bar comes down, touches the hooks, drops one pixel too far and comes back, the word squashes on landing and the bar rings like a string, less and less. With every rep the word is heavier. On the eighth the last tick is vermilion, the bar turns vermilion and ‘Set done’ takes the place of ‘Reps’; then the weight comes off, the word goes light again, the ticks empty from the right and the set starts over. A click (or a tap, or Enter) does one more rep straight away; after a finished set it starts a new one.

How it works

One drawing

Hooks, floor, bar, plates and word sit in an SVG as big as the window, redrawn on every frame by a single function. GSAP only animates numbers in an object: how high the bar is, how far it sinks on landing, the squash of the word, its two axes and how vermilion it is. The sizes (length of the bar, font size, height of the lift) are worked out from the width and height of the window on every resize.

The bar that bends

The bar is a path with a point every six pixels: the middle sits where GSAP puts it, the ends move by f times u squared, where u goes from minus 1 to 1. The number f is a damped spring on GSAP's ticker (stiffness 380, damping 5.2, four steps per frame) pushed by the acceleration of the bar, which comes from its position in the two frames before. When the bar starts the ends lag, when it stops they overshoot, when it hits the hooks they ring: nothing is drawn by hand, physics does it.

One rep

Every rep is a timeline: a small squash, the lift with power2.out, meanwhile Archivo's two axes go to the next rep's weight, the descent with power2.in, one point two pixels too far and the return with elastic.out, with the word squashing and standing up again. The weight goes from LIGHT to HEAVY in equal steps, the width from 112 to 88 so the darker word does not spread.

The set and the click

Between one rep and the next there is a GSAP delayedCall, not a setTimeout, so everything follows GSAP's clock. A click cancels the wait and does a rep at once; if one comes while the bar is in the air, the rep is queued. On the last one the bar and the last tick turn vermilion, then a timeline takes the weight off and empties the ticks from the right. With reduced motion the bar neither moves nor bends: on every rep only the weight of the word and the counter change.

Values to change

NameFileDefaultWhat it changes
WORDscript.js'LIFT'The word on the bar (short words lift best, up to 7 letters).
REPSscript.js8How many reps make a set.
LIGHTscript.js220The weight of the word before the first rep (100 to 900).
HEAVYscript.js860The weight it reaches on the last rep (100 to 900).
LIFT_TIMEscript.js0.55Seconds to lift the bar to the top.
RESTscript.js0.45Seconds of rest between two reps.
FLEXscript.js1How much the bar bends and rings: 0 is rigid, 2 very whippy.

Try them in the Playground

When to use it

For the opening of a gym's, a workout app's or a personal trainer's site, for a rep or goal counter, for a loading screen that should feel like effort and satisfaction together.

On phones

The same animation with the sizes adapted: the bar and the word are measured on the width and the height of the screen, so they fit the box; a tap does one more rep.

With reduced motion

The bar stays still on its hooks and does not bend: on every rep the word changes weight with a fade and the counter goes up; when the set is done the bar turns vermilion.

Pitfalls

  • A bend drawn with a tween made the bar look rubbery: with a spring pushed by the acceleration the ends only lag when the bar changes speed, as they really do.
  • A long frame (the tab hidden and shown again) gave a huge acceleration and the bar jumped: the step is kept between a 240th and a 20th of a second and the acceleration is limited.
  • GSAP does not animate colours written as var(--accent): the ticks take the colours already computed from :root, so the Playground's Palette still changes them.
  • As it got darker the word spread and touched the plates: the wdth axis goes down with the weight.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 5.5 KB; the libraries: GSAP 28 KB.

Testing

Tested on 22 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 animations