Skip to content
animorio
IT
All components

The pendulum wave button

Buttons, Physics, HoverCanvas, GSAPAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

The Sync devices button with a pendulum wave inside, seen from above: twelve steel balls, each hanging from two threads, shorter and shorter from left to right. Under the pointer an orange bar pushes them all aside; at a click it lets them go. They start together, then each falls one step behind its neighbour and the row turns into a snake, two rows, three, a tangle, until after exactly six seconds they are all in line again, at the same instant, and the bar catches them: In sync.

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 pendulum wave button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/ibm-plex-sans@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the pendulum wave button: the Sync button of a devices app. In its window hangs a pendulum wave seen from
         above, a row of steel balls on threads of different lengths. A bar pulls them aside together and lets go:
         each swings at its own pace, they drift into snakes, rows and knots, and after one cycle exactly they are
         all in line again, where the bar catches them: in sync -->
    <div class="sync" data-sync>
      <button class="sync__btn" type="button" data-btn data-autoclic>
        <span class="sync__well" aria-hidden="true" data-well>
          <canvas class="sync__canvas" data-canvas></canvas>
        </span>
        <span class="sync__label" data-label>
          <span class="sync__icons" aria-hidden="true" data-icons>
            <svg class="sync__icon" viewBox="0 0 24 24" data-arrows>
              <path d="M5.42 9.61A7 7 0 0 1 18.58 9.61"/>
              <path d="M19.81 6.23 18.58 9.61 15.46 7.81"/>
              <path d="M18.58 14.39A7 7 0 0 1 5.42 14.39"/>
              <path d="M4.19 17.77 5.42 14.39 8.54 16.19"/>
            </svg>
            <svg class="sync__icon sync__icon--done" viewBox="0 0 24 24" data-check>
              <path d="M5 12.6l4.4 4.3L19.2 7.2" data-tick/>
            </svg>
          </span>
          <span class="sync__words" data-words>Sync devices</span>
        </span>
      </button>
      <p class="sr" aria-live="polite" data-live></p>
    </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>

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.

20 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 pendulum wave button” from Animorio and fit it into my site

What it does

On a white background a bottle green pill 60 pixels tall, with the sync arrows and Sync devices in a cream sans. Sunk into the left of the pill there is a round-ended window with a bone coloured floor printed like a laboratory card: a tick under every ball at the top and the bottom, and a dashed line. Inside, seen from above, hangs a pendulum wave: twelve polished steel balls hanging from a taut wire across the window, each on two threads in a V, so it can only swing across the row. The threads get shorter from left to right, and the shadows tell it: the balls on the left hang almost on the floor and have short, sharp shadows, those on the right hang higher and their shadows are longer and softer. When the pointer comes, an orange lacquered bar rises from a slot under the window: it runs quickly until it touches the balls, then pushes them all gently aside, up to the dashed line, and holds them there; pressing pushes a little further and the pill sinks. At a click the bar drops below the balls and slides away, and they go: first all together, then each falls one step behind its neighbour, and the row becomes a running wave, two waves, a double snake, two perfect rows halfway through the cycle, three groups, a tangle, and back again; the label says Syncing with the arrows dimmed. In the last half second the bar comes back up under the balls, below them, and at the end of the cycle, when they are all at the top of their swing in one line, it rises into their plane and catches them; it brings them back to the dashed line they left from, a glint runs along the row, the arrows turn away and the tick of In sync draws itself. After a moment the bar lays them down, they sway a little and the bar goes back into its slot. On a phone the finger is the bar: held down it pulls the balls aside, lifted it lets them go. With the keyboard the focus shows the ring and brings the bar up, Enter syncs.

How it works

Tuned pendulums

The pendulum on the left makes SWINGS swings in CYCLE seconds, and each one after it makes one more: pendulum i swings (SWINGS + i) / CYCLE times a second. After one cycle each has made a whole number of swings, so they are all in line again; halfway the even and the odd ones are on the two sides, and in between the phase difference between neighbours grows slowly and draws the waves. The length comes from the frequency, L = g / ω², at one pixel to a millimetre and with the real gravity: from 25 centimetres on the left to 3 on the right.

The exact period

A pendulum swinging wide is slower than the small-swing formula says: its period is the small one times 2K(sin(θ/2))/π, where K is the complete elliptic integral of the first kind, worked out in a few lines with the arithmetic-geometric mean of 1 and cos(θ/2). The shortest ball leaves at 24 degrees: its length is found by repeating the sum eight times, for the average swing it will have over the cycle as the air damps it (DAMPING), and ω is raised a hair because the damping slows it too.

The physics

Every ball is a real pendulum, θ'' = −(g/L) sin θ − c θ', stepped in 1/480 of a second with semi-implicit Euler, with the frame's step capped at 1/30. From the angle come the move on screen, L sin θ, and the height over the floor, which sets the shadow. While syncing c is DAMPING; at rest a stronger brake stops the swaying in a moment. When nothing moves the canvas is not redrawn.

The bar

The bar is a one-sided constraint: up against the balls, none can be below its edge, and those it reaches take its speed; dropped below them, the balls swing over it. It rises in two goes, quickly up to the lowest ball and then with a slow sine.inOut, because hit at speed the slow balls would fly on past it. On release it holds them all against its edge at the same instant, then drops (it darkens and its shadow shortens) and slides away under them.

The catch

The catch runs on the simulation's own clock, not on a tween: in the last LEAD seconds of the cycle the bar comes up under the balls, still below their plane, and reaches the expected top (the starting swing times e^(−c·CYCLE/2)) right when the cycle ends. There it becomes a constraint again and holds them; then it brings them back to the dashed line. A white glint runs along the row, a moving gaussian, and the label changes with the tick drawing itself.

Seen from above

The floor with its grain, ticks and dashed line is drawn once on a canvas of its own. Every ball is painted once on its own little canvas, at its own size (a tenth smaller when it hangs lower): a radial gradient of steel, the bone floor caught in its lower rim and the point of light. Every frame: shadows (moved and softened in step with the height over the floor), bar, V threads, balls, and over everything the taut wire.

Values to change

NameFileDefaultWhat it changes
BUSYscript.jsSyncingThe words while the pendulums swing.
DONEscript.jsIn syncThe words once they are all in line again.
PENDULUMSscript.js12How many pendulums hang in the window.
CYCLEscript.js6The seconds until they are all in line again: how long the sync takes.
SWINGSscript.js6How many swings the longest pendulum makes in that time; each one after it makes one more. With more swings the balls go faster, but the figures stay the same: they depend on CYCLE alone.
SWINGscript.js12.5How far the bar pulls the balls aside before it lets go, in pixels.
DAMPINGscript.js0.1How much the air slows the swing, per second: 0 not at all.
HOLDscript.js1.7The seconds the bar holds the balls in line, saying In sync, before it lays them down.
LIGHTscript.js1How long the shadows on the floor are: 0, the lamp is straight above.

Try them in the Playground

When to use it

For the action that brings several things back into step: syncing devices, folders, calendars, the lights of a home or the speakers of several rooms; and also Align, Calibrate or Rebalance. The cycle that closes by itself tells the wait, so it belongs where the action truly takes a few seconds. It is a button to show off, one to a page, in tools for the home, music, science or teaching.

On phones

The pill is 60 pixels tall and 348 wide, or the screen less 32; the window takes the room beside the words and, if it is narrow, the balls get smaller and closer together. With no pointer the finger is the bar: held down, the bar pulls the balls aside; lifted, it lets them go. If the finger slides away to scroll the page, the bar goes back into its slot.

With reduced motion

No swinging and no bar. At a click the balls all dim and light up again one at a time from left to right, over the time of the cycle, so the sync is still seen to progress; then In sync with the tick, in a fade, and after a moment Sync devices again. At every change the whole label fades out and back in, so the icon does not slide to its new place. The pill pressed darkens a little instead of shrinking.

Pitfalls

  • With the small-swing period the row did not come back into line: the shortest ball, 3 centimetres of thread let go at 24 degrees, ended the cycle almost 3 pixels below the others. With the exact period worked out for the starting swing the gap fell to 0.4 pixels; worked out for the average swing over the cycle, which the air takes down, it is a tenth of a pixel.
  • The bar rising by itself (at a tap, from the preview, with Enter) hit the balls still fast: the slowest flew on past its edge, on release they did not leave from the same height and the first snake started crooked. Now the bar rises in two goes, a quick run up to the lowest ball and a slow push, and on release it holds them all against it.
  • At rest the orange bar lay at the bottom of the window, under the balls, and looked like a progress bar: now it goes back into a slot under the rim and comes out only for the pointer, a touch or the focus.
  • Shadows worked out from the real height became huge blobs with few SWINGS or a long CYCLE, when the threads reach two metres: now the row's heights always spread over the same span of shadow, from the shortest pendulum to the longest.
  • The air takes a quarter of the swing away in six seconds, and the balls caught at the end sat 3 pixels under the dashed line they left from, as if they had not quite come back: once the bar has caught them, it brings them back to the line.
  • With the label aligned left, Syncing and In sync, shorter than Sync devices, left a green gap on the right; and if the label changed width the window would change too, with the balls jumping sideways. Now the label has the width of the longest words, the icon and the words sit in the middle and the icon slides to its new place.

Libraries

GSAP 3.15.0, IBM Plex Sans (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