Skip to content
animorio
IT
All components

The knot button

Buttons, SVG, PhysicsGSAPMediumCompressed weight: Demo 5.5 KBGSAP 28 KBFreeDownload the zip

In the minimal style, on white: the Remind me of a live stream, with a thin thread with frayed ends running under the pill. Under the pointer the thread sags and starts to curl. Pressed, the thread makes a loop and pulls it tight into a knot, like a knot tied round a finger so as not to forget: the ends slide in to the middle, the knot turns vermilion, the thread trembles once like a taut string and Reminder set lands with a tick. Pressed again, the knot comes undone and the thread goes straight.

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 knot button</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">
    <!-- the knot button: the Remind me of a live stream, on white. Under the pill runs a thin thread with frayed ends.
         Pressed, the thread curls into a loop and pulls it tight into a knot, like a knot tied round a finger so as not
         to forget; the knot turns vermilion, the thread trembles and Reminder set lands. Pressed again, it comes undone -->
    <div class="knot" data-knot>
      <div class="knot__frame" data-frame>
        <button class="knot__btn" type="button" data-btn data-autoclic aria-pressed="false">
          <span class="knot__words">
            <span class="knot__word" data-word="rest">
              <svg class="knot__bell" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 2.2c-2.3 0-3.9 1.8-3.9 4.1v2.6L2.8 11h10.4l-1.3-2.1V6.3C11.9 4 10.3 2.2 8 2.2Z M6.6 12.6a1.5 1.5 0 0 0 2.8 0"></path></svg>
              <span data-rest-text>Remind me</span>
            </span>
            <span class="knot__word knot__word--done" data-word="done" aria-hidden="true">
              <svg class="knot__tick" viewBox="0 0 16 16" aria-hidden="true"><path d="M2.6 8.6 6.3 12.1 13.5 4.3" data-tick></path></svg>
              <span data-done-text>Reminder set</span>
            </span>
          </span>
        </button>
        <svg class="knot__thread" aria-hidden="true" data-thread>
          <path class="knot__fray" data-fray></path>
          <path class="knot__line" data-line></path>
          <path class="knot__over" data-over></path>
        </svg>
      </div>
      <p class="knot__note" data-note>Live stream · Thu 6 Nov, 18:00</p>
      <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.

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

What it does

On a white background a pill 60 pixels tall with a thin light grey outline and inside a small line drawn bell and Remind me in Archivo; under the pill runs a thin black thread, longer than the pill, its ends frayed into three hairs; below that the small line Live stream · Thu 6 Nov, 18:00. Under the pointer the outline turns black, the words widen and gain weight on a spring, and the thread sags a little and starts to curl in the middle, as if about to make a knot. Pressed, the pill gives and springs back; the thread rolls into a real loop under the pill, then pulls it tight: the two ends slide in to the middle, the loop closes into a small knot that turns vermilion, and the whole thread trembles once, like a string pulled taut. The words swap: Reminder set lands with a vermilion tick, wide and light first, then narrow and heavy, and the line below says We’ll ping you an hour before. Pressed again, the knot loosens, becomes a loop and the loop unrolls into a straight thread.

How it works

A thread that is a cycloid

The thread is an SVG path of 220 points rebuilt on every frame. In a window D wide in the middle, each point moves forward by A times sin(φ) and down by A times (1 − cos φ), with φ going from 0 to 2π across the window: a prolate cycloid. When A is larger than D over 2π the thread runs backwards at the bottom of the curve and crosses itself near the line: that is the loop.

Pulling the knot tight

Tightening is the same curve with a smaller A and D (from LOOP to KNOT pixels, and the window from 30 to 11), while the two ends move in towards the middle by what the loop gives back, so the thread seems to keep its length. GSAP moves four numbers: curl, tight, the tremble and the opacity of the vermilion stroke, which redraws only the knot window over the black thread.

The tremble

When the knot closes, every point gets an extra vertical sin(π s) times sin(3π s) times an amplitude GSAP takes from TREMBLE to zero with elastic.out: the string swings and stops. The same curve, smaller, when the knot comes undone.

The words

Archivo has width and weight axes: under the pointer Remind me goes to 108 and 480; Reminder set comes in wide at 124 and weight 300 and lands narrow at 98 and weight 600 with back.out, while the tick draws itself with the dash offset.

Accessible

It is a real button with aria-pressed; an aria-live line announces the reminder; the keyboard focus ring shows only with the keyboard. While the knot is moving clicks are ignored. With reduced motion the thread fades from straight to knotted and the words swap without bounces.

Values to change

NameFileDefaultWhat it changes
LABELscript.jsRemind meThe words at rest.
DONEscript.jsReminder setThe words once the knot is tied.
NOTEscript.jsLive stream · Thu 6 Nov, 18:00The small line under the pill.
NOTE_DONEscript.jsWe’ll ping you an hour beforeThe small line once the knot is tied.
LOOPscript.js13How big the loop gets before it tightens, in pixels.
KNOTscript.js4.2How big the knot stays once pulled tight, in pixels.
TIEscript.js0.55The seconds the thread takes to curl into a loop.
PULLscript.js0.5The seconds the loop takes to pull tight.
TREMBLEscript.js3How far the thread trembles when the knot closes, in pixels.

Try them in the Playground

When to use it

For a button that remembers something: the reminder for a live stream or an event, the alert when a product is back, a list saved for later, an article’s bookmark.

On phones

The pill narrows to 42 pixels from the edges and the thread is worked out again on the real width, always a little longer than the pill. A tap ties and unties the knot like a click; the hover curl happens only with a mouse.

With reduced motion

The thread fades from straight to knotted, the knot turns vermilion without trembling and the words swap without bounces.

Pitfalls

  • A loop with a single formula: without the prolate cycloid it took hand made Bézier curves for each moment; with A and D as the only numbers the loop is born, grows and tightens smoothly, and going from straight thread to knot is just a tween.
  • The thread should seem to keep its length while the loop tightens: the ends move in towards the middle by (LOOP × 1.9 − KNOT × 1.6) times curl, roughly the thread the loop gives back.
  • The knot window is drawn twice, black in the thread and vermilion on top with its own opacity: colouring the whole thread for a knot a few pixels wide did not read.
  • The thread has to be worked out again when the typeface arrives and when the pill changes size (ResizeObserver), because the ends always sit a little past the pill.

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 23 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