Skip to content
animorio
IT
All components

The hold on the shrinking line

Timers, Physics, Text, SVGGSAPAdvancedCompressed weight: Demo 9.8 KBGSAP 28 KBFreeDownload the zip

In the minimal style: the time a concert’s seats are held for, in large figures, with a thin line under it that gets shorter. The minute ticks hang from the line and drop one at a time as the line pulls back past them, while the line, rid of their weight, flicks and ripples like a string; the figures lose weight with the time, from very bold to a hairline, without getting any narrower. In the last thirty seconds the accent, at zero “Hold expired” and “Hold again”.

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 hold on the line</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/roboto-flex@5.3.0/full.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the seat hold of a concert checkout: the time left is a thin line that gets shorter, the minute ticks hang
         from it and drop off one at a time as the line pulls back past them, and the figures lose weight as the
         time runs out. In the last seconds the accent; at zero the hold has expired and can be taken again -->
    <section class="hold" aria-label="Seat hold" tabindex="-1" data-hold data-autoclic>
      <header class="hold__top">
        <span class="hold__swap" data-label>Seats held for</span>
        <span class="hold__swap hold__swap--meta" data-meta>2 seats · Row F</span>
      </header>

      <p class="hold__time" role="timer" aria-live="off" aria-atomic="true">
        <span class="hold__digits" aria-hidden="true" data-digits></span>
        <span class="sr" data-time-words></span>
      </p>

      <div class="hold__rule" data-rule>
        <svg class="hold__svg" aria-hidden="true" data-svg>
          <path class="hold__track" data-track/>
          <path class="hold__line" data-line/>
          <g class="hold__ticks" data-ticks></g>
        </svg>
      </div>

      <div class="hold__foot">
        <span class="hold__swap hold__swap--note" data-note>Finish checkout to keep them.</span>
        <button class="hold__again" type="button" hidden data-again>
          <span class="hold__fill" aria-hidden="true"></span>
          <span class="hold__words">Hold again</span>
        </button>
      </div>

      <p class="sr" aria-live="polite" data-live></p>
    </section>
  </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.

24 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 hold on the shrinking line” from Animorio and fit it into my site

What it does

On a black background, at the top left Seats held for and on the right, in grey, 2 seats · Row F. Under it the time in very large figures, and lower down a thin white line as long as the component, with ten ticks hanging under it: a longer one at the start and one for every minute, closer and closer towards the right, because the line measures the time on a curve and the last minutes get more room. Behind the line, barely visible, the track of the whole hold stays. At the foot, in grey, Finish checkout to keep them. When the page opens the words rise out of a slot, the line grows from the left, the ticks drop onto it one at a time as soon as the line reaches them, and where they land the line gives and ripples; meanwhile the figures count from 0:00 to 10:00 and gain weight up to a very narrow black. Then the time runs: the line pulls back to the left, faster and faster as the time runs out, and every time it passes a tick the tick has nothing left to hang from and drops, turning a little, and fades; the end of the line flicks up and a light wave runs back along it. Every minute the minutes figure drops out of its window and the new one comes down into its place. The figures get lighter with the line, and at the same time as much wider as it takes to stay just as wide. In the last thirty seconds figures, line and ticks turn orange. At zero the last tick drops, the long one at the start; the words change to Hold expired, Seats released and Your seats went back on sale., the 0:00 stays drawn in a grey hairline and next to the words at the foot the Hold again button rises in. Under the mouse the button fills with white with a small bounce and its words widen; pressed, it goes and the hold starts again as when the page opened. In the library’s preview the time runs much faster, so the whole round can be seen.

How it works

The line measures the time on a curve

The share of the line that is left is (time left / HOLD) to the power of CURVE. With CURVE under 1 the last minutes get more room, the ticks grow closer towards the right as on a slide rule and the end of the line runs faster and faster as the time runs out: at 0.55 the last thirty seconds of ten minutes are still almost a fifth of the line. The ticks sit where the curve puts the minutes, every MARK seconds.

A string with a free end

The line is an SVG path redrawn on every frame from 120 points of a string: the wave equation, worked out in small steps (the step stays under the stability limit) with damping. The start is held; the end is where the line ends, and it is free, because the points past the tip copy the last one. When a tick drops the string gets a bell-shaped push upwards where it hung, when a tick lands one downwards: the tip flicks and the wave runs back. Once the string is still the path goes back to a single straight line and nothing is worked out.

Ticks that really fall

Every tick hangs from the point of the string above it and follows it. When the line passes it, it comes off: it falls under gravity, with a small push backwards and a turn, because the line pulling back drags its top, and fades within a hundred pixels or so. Each one has a small fixed difference of its own, so every drop is different but the same from one round to the next. When the line stretches out again the ticks drop onto it from above as soon as it passes under them.

The weight goes, the width stays

The figures are in Roboto Flex in its display cut (opsz 144) and their weight (wght) follows the line, from WEIGHT[0] to WEIGHT[1]. A lighter typeface is also a narrower one: as soon as the font is in, for every weight the script finds by bisection the width (wdth) that keeps the ten figures as wide as at the full weight, and uses it on every frame. So the time thins out on the spot, from a very narrow black to a wide hairline, and nothing around it moves.

The figures and the direction

Every figure sits in a window as tall as its ink: the old one drops down and the new one comes down with a back.out. The more often a figure changes, the faster it rolls, so it never lags behind the time; under a seventh of a second it simply changes. The time left is counted from the clock, so a tab left in the background still ends on time. The preview’s click (detail 0) makes the time run PREVIEW times faster, then slower in the last seconds, and when the hold expires it presses Hold again by itself.

Values to change

NameFileDefaultWhat it changes
HOLDscript.js600How many seconds the seats are held for.
MARKscript.js60Every how many seconds a tick hangs from the line.
LASTscript.js30The last seconds, in the accent colour.
CURVEscript.js0.55How the line measures the time: 1 = evenly; under 1 the last minutes get more room and the tip runs faster and faster.
WEIGHTscript.js[860, 110]The weight (wght) of the figures with all the time ahead and at zero.
SPEEDscript.js1How fast the time goes by: 1 = real time.
PREVIEWscript.js[180, 9]In the library’s preview the time runs faster: how many times, and how many in the last seconds.
STRINGscript.js[1.6, 3.2]The line as a string: how fast a ripple runs along it and how soon it calms down.
ENDEDscript.js['Hold expired', 'Seats released', 'Your seats went back on sale.']The words once the time is up: top left, top right and at the foot.

Try them in the Playground

When to use it

For the time a ticket site holds the seats chosen, for a table or room booking waiting for payment, for a cart with the goods set aside, for a code that expires, for an auction about to close.

On phones

The component takes the whole width less the margins (up to 360 pixels) and the figures get smaller with the phone while staying the same; the line, the ticks and the string are worked out again for the new size, even halfway through a fall. The button answers a touch as it does the mouse: it fills and its words widen while the finger presses.

With reduced motion

Nothing moves: the line still gets shorter, because it is the time, but the ticks it passes fade where they are, the string does not ripple, the figures change without dropping and the words change with a fade. The weight of the figures still goes down. Hold again puts the whole line back, and it fades in with its ticks.

Pitfalls

  • On a line in proportion to the time, the last thirty seconds of ten minutes were sixteen pixels, and the tensest part could not be seen: the line measures the time on a curve (CURVE), which gives the end room.
  • As the weight went down the figures got narrower and the whole number visibly shrank: for every weight the width (wdth) that keeps the figures just as wide is found, once. It is measured with a fixed opsz, so it holds at every size of the type.
  • With the preview’s fast time the minutes figure changed every third of a second but rolled for half a second, and for a moment the old minute could be read with the new seconds: the length of the roll follows the pace of the changes.
  • The very narrow black figures piled up into a blot as they swapped: every figure has a window as tall as its ink, measured from the font’s metrics, and the one leaving is already out when the new one comes in.
  • Landing ticks started too high and passed in front of the figures: they start from just above the line.
  • The preview’s video stops when the demo stands still: the fast round, the wait on the expired time and Hold again pressed by itself are all GSAP tweens and timelines.

Libraries

GSAP 3.15.0, Roboto Flex (font variabile) 5.3.0.

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

Testing

Tested on 16 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