Skip to content
animorio
IT
All components

The needle and thread steps

Steppers, Transitions, Physics, SVGGSAP, CSS onlyAdvancedCompressed weight: Demo 19 KBGSAP 28 KBFreeDownload the zip

A made-to-measure shirt order sewn by hand on a strip of navy wool: going on, the needle runs a seam to the next step, slips under it while its ring closes and the step left behind is embroidered in satin stitch; going back, the seam jumps off as one rippling thread and is yanked back; at the end the thread is pulled, the cloth gathers into folds, the thread is cut and the cloth springs back.

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 needle and thread steps</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/encode-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the steps of a made-to-measure shirt order, sewn by hand on a strip of navy wool. Going on, the needle goes
         into the cloth and runs a seam of stitches to the next step, slips under its patch (the ring closes round it)
         and the step left behind is embroidered in satin stitch. Going back, the needle lifts and slides back, the
         stitches come out and the loose thread whips. At the end the seam is knotted, the thread pulled until the
         cloth gathers, then cut: the cloth springs back -->
    <div class="order" data-order data-autoclic>
      <div class="strip" data-strip>
        <span class="strip__cloth" aria-hidden="true" data-cloth></span>
        <svg class="strip__under" aria-hidden="true" data-under></svg>
        <ol class="steps" aria-label="Shirt order" data-steps>
          <li class="step" data-step>
            <button class="step__hit" type="button" data-hit>
              <svg class="step__patch" viewBox="0 0 40 40" aria-hidden="true">
                <circle class="step__chalk" cx="20" cy="20" r="17.5"/>
                <g class="step__satin" data-satin></g>
                <path class="step__ring" d="M2.5 20A17.5 17.5 0 0 1 37.5 20" data-ring/>
                <path class="step__ring" d="M2.5 20A17.5 17.5 0 0 0 37.5 20" data-ring/>
                <g class="step__icon" transform="translate(9 9) scale(0.9167)">
                  <path d="M5.5 5h13v10.6l-1.63 1.65-1.62-1.65-1.63 1.65-1.62-1.65-1.63 1.65-1.62-1.65-1.63 1.65-1.62-1.65z"/>
                  <path d="M8.6 12.6l4.6-4.6M12.4 12.6l3-3"/>
                </g>
              </svg>
              <span class="step__name" data-name>Fabric</span>
            </button>
          </li>
          <li class="step" data-step>
            <button class="step__hit" type="button" data-hit>
              <svg class="step__patch" viewBox="0 0 40 40" aria-hidden="true">
                <circle class="step__chalk" cx="20" cy="20" r="17.5"/>
                <g class="step__satin" data-satin></g>
                <path class="step__ring" d="M2.5 20A17.5 17.5 0 0 1 37.5 20" data-ring/>
                <path class="step__ring" d="M2.5 20A17.5 17.5 0 0 0 37.5 20" data-ring/>
                <g class="step__icon" transform="translate(9 9) scale(0.9167)">
                  <path d="M8.4 4.2 4.6 6 2.8 11.2l2.9 1.1 1.2-2.6v10h10.2v-10l1.2 2.6 2.9-1.1L19.4 6l-3.8-1.8"/>
                  <path d="M8.4 4.2 9.6 9 12 7.4l2.4 1.6 1.2-4.8C13.4 5.2 10.6 5.2 8.4 4.2zM12 7.4v12.3"/>
                </g>
              </svg>
              <span class="step__name" data-name>Collar</span>
            </button>
          </li>
          <li class="step" data-step>
            <button class="step__hit" type="button" data-hit>
              <svg class="step__patch" viewBox="0 0 40 40" aria-hidden="true">
                <circle class="step__chalk" cx="20" cy="20" r="17.5"/>
                <g class="step__satin" data-satin></g>
                <path class="step__ring" d="M2.5 20A17.5 17.5 0 0 1 37.5 20" data-ring/>
                <path class="step__ring" d="M2.5 20A17.5 17.5 0 0 0 37.5 20" data-ring/>
                <g class="step__icon" transform="translate(9 9) scale(0.9167)">
                  <path d="M3.9 15.6 15.6 3.9l4.5 4.5L8.4 20.1z"/>
                  <path d="M7.4 12.1l1.8 1.8M10 9.5l1.2 1.2M12.6 6.9l1.8 1.8"/>
                </g>
              </svg>
              <span class="step__name" data-name>Fit</span>
            </button>
          </li>
          <li class="step" data-step>
            <button class="step__hit" type="button" data-hit>
              <svg class="step__patch" viewBox="0 0 40 40" aria-hidden="true">
                <circle class="step__chalk" cx="20" cy="20" r="17.5"/>
                <g class="step__satin" data-satin></g>
                <path class="step__ring" d="M2.5 20A17.5 17.5 0 0 1 37.5 20" data-ring/>
                <path class="step__ring" d="M2.5 20A17.5 17.5 0 0 0 37.5 20" data-ring/>
                <g class="step__icon" transform="translate(9 9) scale(0.9167)">
                  <path d="M5.5 6.5h13a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-13a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2z"/>
                  <path d="M3.5 10.2h17M7 14.4h3.6"/>
                </g>
              </svg>
              <span class="step__name" data-name>Payment</span>
            </button>
          </li>
        </ol>
        <!-- the needle and the loose thread, drawn by the script -->
        <svg class="strip__over" aria-hidden="true" data-over></svg>
      </div>

      <div class="foot">
        <p class="foot__say" aria-live="polite"><span class="foot__line" data-say>Choose the cloth for your shirt</span></p>
        <div class="foot__keys">
          <button class="key key--back" type="button" data-back>
            <span class="key__bg" aria-hidden="true"></span>
            <span class="key__clip"><span class="key__text">Back</span></span>
          </button>
          <button class="key key--next" type="button" data-next>
            <span class="key__bg" aria-hidden="true"></span>
            <span class="key__clip"><span class="key__text" data-next-text>Continue</span></span>
          </button>
        </div>
      </div>
    </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.

32 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 needle and thread steps” from Animorio and fit it into my site

What it does

On a black background, alone, a strip of midnight navy wool with its top and bottom edges cut in a zigzag by pinking shears, 84 pixels tall, with the four steps of a made-to-measure shirt order: Fabric with a cloth swatch, Collar with a shirt and its collar, Fit with a ruler and Payment with a card. Under it, a line saying the step (Choose the cloth for your shirt) and the buttons Back, a dark outline, and Continue, red with a running stitch just inside its edge like a sewn label; on hover the pill swells with a bounce and the words widen on the width axis without moving the other button. The steps to do are marked in tailor’s chalk, a pale dashed circle and the icon in chalk, joined by a chalk line; the current step has a ring of red stitches and the steel needle stuck in the cloth beside it, with a loop of red thread hanging from the ring to its eye; the steps done are embroidered in satin stitch, close slanting red threads with the icon left bare in the middle. On arrival the needle comes in from the left, sews the stretch up to Fabric and is stuck beside it. Going on, the needle comes out, lies down on the chalk line and sews: at every gap its tip bites into the cloth, and behind its eye the thread lies down as stitches; at the next step it slips underneath, and the ring closes from both sides as it goes through, then it comes out and is stuck in the cloth again; meanwhile the step left behind is embroidered, one silk thread after another, to and fro. Going back, the needle tugs and the whole seam jumps up at once, a single rippling thread; the needle is pulled back eye first while the thread is wound into the step reached, rippling and then taut, the rings and the embroidery it crosses come undone, and at the end the loop of thread under the eye is left. A step done can be clicked to go back to it, several steps at a time too. On the last step the button becomes Place order: the needle sews to the end of the strip, loops round the end of the seam and pulls; the cloth gathers on the thread, the steps crowd together, the patches turn oval, the names narrow on the font’s width axis and folds come up in the wool; then the thread is cut, the end left behind curls up, the cloth springs back to its length with a small jolt, the needle is stuck upright at the end and a light runs over the silk embroidery, one patch after the other. The line says Order placed. Cutting starts Monday and the Start a new order button makes the whole seam jump off in a single wave and takes it back to Fabric.

How it works

Drawn from a few numbers

Over and under the steps are two SVGs the script draws again on every frame, only while something moves. Each seam has one number, how far it is sewn: its stitches, spread evenly from one ring to the next, are written as strokes up to there, and the chalk shows only beyond. The ring is two arcs that leave from nine o’clock and meet at three: each arc is a row of stitches written into a stroke-dasharray that stops where the ring has got to. The satin stitch is a row of slanting lines, each revealed with stroke-dashoffset, one after the other and each the other way round from the last, like the needle going to and fro.

The needle sewing

The needle is a steel spindle with a gradient and its eye, and a blurred shadow. Sewing, GSAP carries it along the line and the eye decides how far the seam has got; at every gap between two stitches the tip goes into the cloth: a clipPath hides the part inside, on a sine curve, while the shadow comes closer. Under the next step’s patch the same clipPath hides the stretch passing underneath, and the ring closes as the eye goes through. Stuck in the cloth, the shadow is no longer moved as a whole but slanted with a matrix: it touches the needle at the tip and leaves it towards the eye, which stays raised.

The thread is a string of points

The thread between the ring and the eye is a string of 36 points worked out in steps of 1/120 of a second (verlet), with the frame step capped at 1/30: each point has a little weight towards the bottom of the strip, the string is stretched between its two ends and each point leans towards the middle of its neighbours, so it bends in soft curves instead of zigzags. Going back, the seam disappears and in its place the string lies along the same line with a push in waves; the needle is pulled back on a curve that speeds up while the string’s length shrinks more slowly: the spare thread ripples and is wound in until the usual loop is back.

The cloth is a chain of springs

At the end the strip becomes a chain: one weight for each step, plus the end, joined by springs that are soft when squeezed (the wool folds) and stiff when stretched (the wool does not give), with the start held by the knot. Pulling the thread shortens the rest length of every spring down to GATHER: the steps crowd to the left, the cloth shortens with scaleX, the patches squash with the scale property, the names narrow with font-stretch on the font’s wdth axis and between each pair of weights two folds come up, dark gradients whose opacity follows how squeezed the cloth is. Once the thread is cut the springs are long again and the cloth opens up to the small jolt of the stiff spring.

Keys, reduced motion and the preview

The steps are an ordered list, the current one has aria-current step; the steps done are buttons that go back, the others are disabled, and each has an aria-label saying whether it is done, current or still to do. Every press counts: the moves wait their turn (three at most) and the one playing speeds up; while the order is being placed Continue waits. With reduced motion the needle does not travel: it fades out and back in at its new place, and only the stitches, rings and embroidery that change fade. The library’s preview clicks the component (a click with no hand): on, on, back, on to the order and a new one.

Values to change

NameFileDefaultWhat it changes
STITCHscript.js7The length of a stitch on the cloth, in pixels (the gaps are about half as long).
SEWscript.js0.05The seconds the needle takes for each stitch, going on.
UNPICKscript.js0.42The seconds the thread takes to come out of one seam, going back.
GATHERscript.js0.86How short the cloth gets when the thread is pulled at the end (1: it does not gather).
SPRINGscript.js1How lively the cloth is when it springs back after the cut (0.5 calm, 2 rubbery).
SLACKscript.js9The loose thread between the ring and the needle at rest, in pixels.
LINESscript.js['Choose the cloth for your shirt', 'Pick a collar to go with it', …]The line under the strip, one for each step.
DONEscript.js'Order placed. Cutting starts Monday'The line once the order is placed.

Try them in the Playground

When to use it

For a procedure in a few steps that should feel crafted: a made-to-measure order, a configurator, signing up, a purchase in a makers’ shop. The thread says how far you have got, what is done stays embroidered, and the end really feels finished.

On phones

The strip takes the width of the screen less 16 pixels a side (358 on a 390 phone) and stays 84 tall: the steps stay in a row with their icons, the names drop to 12.5 pixels and never touch, the seams between two steps have four stitches instead of nine. The line goes on top, centred, and Back and Continue share the width, 44 pixels tall. Each step done takes a tap over its whole column.

With reduced motion

The needle does not travel and the thread does not ripple: the needle fades out of its place and back in at the new one, and only the seams, rings and embroidery that change fade, while the rest stays still. At the end no gathering and no light on the silk: the last seam, the cut end and the upright needle fade in. The buttons do not swell, only their edge and colour change.

Pitfalls

  • Going back, the thread pulled out was meant to ripple behind the needle, but with the eye in front the needle itself covered it all: nothing showed. Now the whole seam jumps up as one thread, tied to the step reached, and the needle pulls the other end.
  • The cut end of the thread was a string like the others, with a set curl and a set direction: the two rules fought over the points and the end never came to rest, keeping the loop awake for ever. Now it is a curve drawn from three numbers (direction, curl, length) that GSAP brings to its place with a bounce.
  • With a little weight, in a view from above, the cut end swung like a pendulum and stuck out of the cloth. The ends now have no weight; only the thread between ring and needle has a little, to bend into a loop.
  • Without stiffness, the short thread at rest with many points folded in zigzags. Each point now leans a little towards the middle of its neighbours at every step, and the thread makes soft curves.
  • A shadow moved as a whole made the needle look laid on the cloth, not stuck in it. Now the shadow is slanted with a matrix: it meets the needle at the tip and leaves it towards the eye.
  • The cloth’s spring was too lightly damped: two seconds after the cut the strip was still gathering. Now the spring is soft when squeezed and stiff when stretched, with more friction: it opens and stops with a small jolt.
  • The patch grows on hover with a transition on transform, and while gathering the script squashes it on every frame: the transition chased the squash, late. The squash now goes on the scale property, which is separate.
  • The buttons’ words widen on hover on the wdth axis, and widening they pushed the other button away. Now each button keeps the width of its words at rest, measured at wdth 100, and the words widen into the padding; the cut for the rolling words is a clip-path above and below only.
  • The buttons’ background, absolutely positioned, covered only the words and not the padding: in a grid a positioned child with a grid-area takes its cell as reference. The background now stays out of the grid.
  • Resizing halfway through a move, progress(1) closed the move and started the queued one, whose reference was then lost at once. Now the queue is emptied before closing.

Libraries

GSAP 3.15.0, Encode Sans (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 19 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