Skip to content
animorio
IT
All components

The label maker tags

Tags, Text, TransitionsCanvas, GSAPAdvancedCompressed weight: Demo 20 KBGSAP 28 KBFreeDownload the zip

A tag field made with an old embossing label maker: as you type, the wheel of letters turns, the knob comes down and every letter is pressed into a strip of glossy plastic tape coming out of the machine, white where the plastic stretches. Enter snaps the blade: the label cut off flies onto the cardboard box and its corners curl up a little. Tapped, a label peels off from a corner and is crumpled away. Three tapes to choose from: red, blue and black.

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 label maker tags</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 tags of a box, made with an old embossing label maker: typing turns the wheel and presses every letter
         into a strip of glossy plastic tape that comes out of the machine, the letters white where the plastic is
         stretched. Enter (or the red lever) cuts the tape and the label flies onto the box, its corners curling a
         little; a label tapped is peeled off and crumpled away. The labels and the tape are painted by the script -->
    <div class="maker" data-maker data-autoclic>
      <p class="maker__ask" id="maker-ask">Label this box</p>
      <ul class="board" aria-labelledby="maker-ask" data-board></ul>

      <div class="bench" data-bench>
        <div class="bench__track" aria-hidden="true" data-track><span class="bench__ruler"></span></div>
        <span class="bench__ghost" aria-hidden="true" data-ghost>Type a label</span>
        <span class="bench__caret" aria-hidden="true" data-caret></span>
        <canvas class="bench__tape" aria-hidden="true" data-tape></canvas>

        <!-- the label maker, seen from above: the wheel of letters with its press knob, the pointer, the snout the
             tape comes out of, the red cutter lever, the window on the tape cartridge -->
        <svg class="machine" viewBox="0 0 200 124" aria-hidden="true" focusable="false" data-machine>
          <defs>
            <filter id="mk-soft" x="-20%" y="-200%" width="140%" height="500%"><feGaussianBlur stdDeviation="4.5"/></filter>
            <filter id="mk-softer" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.6"/></filter>
            <linearGradient id="mk-body" x1="0" y1="0" x2="0" y2="1">
              <stop class="st-body-hi" offset="0"/>
              <stop class="st-body" offset="0.42"/>
              <stop class="st-body-deep" offset="1"/>
            </linearGradient>
            <linearGradient id="mk-rim" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0" stop-color="#ffffff" stop-opacity="0.34"/>
              <stop offset="0.35" stop-color="#ffffff" stop-opacity="0.04"/>
              <stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
            </linearGradient>
            <linearGradient id="mk-chrome" x1="0" y1="0" x2="1" y2="1">
              <stop offset="0" stop-color="#fbfcfd"/>
              <stop offset="0.28" stop-color="#a6acb4"/>
              <stop offset="0.5" stop-color="#eef0f2"/>
              <stop offset="0.74" stop-color="#7b818a"/>
              <stop offset="1" stop-color="#d6dade"/>
            </linearGradient>
            <linearGradient id="mk-chrome-v" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0" stop-color="#f4f6f8"/>
              <stop offset="0.5" stop-color="#9aa0a8"/>
              <stop offset="1" stop-color="#d9dde1"/>
            </linearGradient>
            <radialGradient id="mk-disc" cx="0.42" cy="0.36" r="0.7">
              <stop offset="0" stop-color="#34353a"/>
              <stop offset="1" stop-color="#101113"/>
            </radialGradient>
            <radialGradient id="mk-knob" cx="0.36" cy="0.3" r="0.78">
              <stop offset="0" stop-color="#ffffff"/>
              <stop offset="0.22" stop-color="#e3e6e9"/>
              <stop offset="0.62" stop-color="#9ba2aa"/>
              <stop offset="1" stop-color="#5a6068"/>
            </radialGradient>
            <linearGradient id="mk-red" x1="0" y1="0" x2="0" y2="1">
              <stop class="st-red-hi" offset="0"/>
              <stop class="st-red" offset="0.55"/>
              <stop class="st-red-deep" offset="1"/>
            </linearGradient>
            <radialGradient id="mk-glare" cx="0.3" cy="0.25" r="0.8">
              <stop offset="0" stop-color="#ffffff" stop-opacity="0.55"/>
              <stop offset="0.45" stop-color="#ffffff" stop-opacity="0.06"/>
              <stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
            </radialGradient>
            <clipPath id="mk-window"><circle r="17.6"/></clipPath>
          </defs>

          <ellipse class="mk-shadow" cx="104" cy="114" rx="90" ry="7" filter="url(#mk-soft)"/>

          <g data-jolt>
            <!-- the snout: the tape comes out of its left end, between two chrome jaws -->
            <path class="mk-snout" d="M46 44H14C8.5 44 6 47 6 52V72C6 77 8.5 80 14 80H46Z"/>
            <path class="mk-snout-rim" d="M46 44.6H14C9 44.6 6.6 47.4 6.6 52"/>
            <rect class="mk-jaw" x="5" y="44.5" width="5.4" height="7" rx="1.6"/>
            <rect class="mk-jaw" x="5" y="72.5" width="5.4" height="7" rx="1.6"/>
            <rect class="mk-slot" x="4.4" y="50.6" width="2.2" height="22.8" rx="1"/>

            <!-- the cutter lever, turning round its pin -->
            <g transform="translate(42 93)">
              <g data-lever>
                <path class="mk-arm" d="M2 -3.2L-26 -2.4C-28.4 -2.3 -29 -1 -29 0.6V1.6C-29 3 -28.4 4 -26 4L2 3.6Z"/>
                <rect x="-38.5" y="-6.5" width="14" height="13.5" rx="3.4" fill="url(#mk-red)"/>
                <path class="mk-paddle-shine" d="M-36.4 -4.4H-27"/>
              </g>
              <circle class="mk-pin" r="3.6"/>
            </g>

            <!-- the body -->
            <path class="mk-body" d="M62 10H160C182 10 196 30 196 62C196 94 182 114 160 114H62C44 114 32 99 32 82V42C32 25 44 10 62 10Z"/>
            <path class="mk-body-rim" d="M62 10H160C182 10 196 30 196 62C196 94 182 114 160 114H62C44 114 32 99 32 82V42C32 25 44 10 62 10Z"/>
            <path class="mk-body-edge" d="M196 62C196 94 182 114 160 114H62C44 114 32 99 32 82"/>

            <!-- ribs of the grip -->
            <g class="mk-ribs">
              <path d="M184.5 46V78"/>
              <path d="M188.5 48V76"/>
              <path d="M192.2 51V73"/>
            </g>

            <!-- the window on the cartridge: the roll of tape turns as the tape is fed -->
            <g transform="translate(160 62)">
              <circle class="mk-recess" r="19.5"/>
              <g clip-path="url(#mk-window)">
                <g data-coil>
                  <g data-coil-spin>
                    <circle class="mk-coil" r="15.4" data-coil-fill/>
                    <circle class="mk-wrap" r="13.6"/>
                    <circle class="mk-wrap" r="11.8"/>
                    <circle class="mk-wrap" r="10"/>
                    <circle class="mk-hub" r="7.4"/>
                    <path class="mk-spoke" d="M0 -7V-3M6.06 3.5L2.6 1.5M-6.06 3.5L-2.6 1.5"/>
                    <circle class="mk-hole" r="2.6"/>
                  </g>
                </g>
                <g data-coil>
                  <g data-coil-spin>
                    <circle class="mk-coil" r="15.4" data-coil-fill/>
                    <circle class="mk-wrap" r="13.6"/>
                    <circle class="mk-wrap" r="11.8"/>
                    <circle class="mk-wrap" r="10"/>
                    <circle class="mk-hub" r="7.4"/>
                    <path class="mk-spoke" d="M0 -7V-3M6.06 3.5L2.6 1.5M-6.06 3.5L-2.6 1.5"/>
                    <circle class="mk-hole" r="2.6"/>
                  </g>
                </g>
                <circle r="17.6" fill="url(#mk-glare)"/>
              </g>
              <circle class="mk-bezel" r="18.8"/>
            </g>

            <!-- the wheel of letters, its chrome ring and the press knob in the middle -->
            <g transform="translate(88 62)">
              <circle class="mk-ring-shadow" r="51" cy="1.6" filter="url(#mk-softer)"/>
              <circle r="50" fill="url(#mk-chrome)"/>
              <g class="mk-knurl" data-knurl></g>
              <circle class="mk-disc" r="45.4" fill="url(#mk-disc)"/>
              <g data-wheel></g>
              <circle class="mk-groove" r="29.6"/>
              <circle class="mk-groove-hi" r="30.6"/>
              <g data-knob>
                <circle class="mk-knob-shadow" r="26" cy="2.2" filter="url(#mk-softer)"/>
                <circle r="25" fill="url(#mk-knob)"/>
                <circle class="mk-knob-line" r="21.5"/>
                <circle class="mk-knob-line mk-knob-line--dark" r="17"/>
                <circle class="mk-knob-line" r="12.5"/>
                <path class="mk-knob-shine" d="M-15 -12A19 19 0 0 1 4 -18.6"/>
              </g>
            </g>

            <!-- the pointer over the wheel: the letter under it is the one pressed -->
            <path class="mk-pointer-base" d="M81.2 3.2H94.8L92.4 9.4H83.6Z"/>
            <path d="M83.4 4.6H92.6L88 16.6Z" fill="url(#mk-red)"/>
          </g>
        </svg>

        <input class="bench__input" id="maker-input" type="text" aria-label="New label" aria-describedby="maker-hint" autocomplete="off" autocapitalize="characters" autocorrect="off" spellcheck="false" enterkeyhint="done" data-input>
        <button class="bench__cut" type="button" aria-label="Cut the label" data-cut></button>
      </div>

      <div class="maker__foot">
        <p class="maker__hint" id="maker-hint" data-hint></p>
        <fieldset class="tapes" data-tapes>
          <legend class="sr">Tape colour</legend>
          <span class="tapes__name" aria-hidden="true">Tape</span>
          <label class="pick" style="--t: var(--red)"><input type="radio" name="maker-tape" value="red" checked><span class="pick__tape">RED</span></label>
          <label class="pick" style="--t: var(--blue)"><input type="radio" name="maker-tape" value="blue"><span class="pick__tape">BLUE</span></label>
          <label class="pick" style="--t: var(--black)"><input type="radio" name="maker-tape" value="black"><span class="pick__tape">BLACK</span></label>
        </fieldset>
      </div>
      <p class="sr" aria-live="polite" data-live></p>
    </div>
  </main>

  <!-- where a label peeled off is crumpled and thrown away -->
  <canvas class="wads" aria-hidden="true" data-wads></canvas>

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

34 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 label maker tags” from Animorio and fit it into my site

What it does

On a white background the words Label this box, a strip of kraft cardboard with three embossed labels stuck on it, a red FRAGILE, a blue KITCHEN and a black BOOKS, glossy, with white raised letters and two corners just lifting. Under it, a vintage label maker seen from above, in graphite plastic and chrome: the wheel of letters with the knob in the middle and a red pointer at the top, the window on the roll of tape, the snout the tape comes out of and a red lever to cut it; next to it, a pale groove with a ruler and the words Type a label. Typing, for every letter the wheel turns the short way until the letter is under the pointer, the tape moves out of the snout, the roll in the window turns, the knob comes down and the letter rises out of the plastic and turns white. Enter, a comma or the lever feed a little more tape, the lever snaps, the blade flashes and the label cut off lifts, goes between the machine and the box and rises into its place at the end of the row; it knocks, squashes slightly and its corners slowly curl. Tapped (or with Enter, Delete, or backspace twice in the empty field), a label lifts a corner that folds over showing its back, comes off, is crumpled into a ball that rolls away and drops, and the others slide over to close the gap. Under it, Tape and three stubs of tape, RED, BLUE and BLACK: the one chosen sticks out, and the roll in the window is swapped.

How it works

A real field under the machine

Over the whole bench there is a real input, transparent, at 16 pixels: the finger taps it, the screen reader reads it. The text is cleaned at every key (capitals, no accents, only the wheel's characters) and stays the truth: the machine follows it a letter at a time, one job at a time in a GSAP timeline, faster when it is behind (timeScale by the letters waiting). The labels are Peel off buttons, the tapes radio buttons, and an aria-live region says what happens.

The wheel and the press

The wheel is an SVG with forty characters in a circle, their tops outwards. For every letter it turns by the shortest difference (normalised between -180 and 180 degrees) with back.out, so the click can be felt; the letter under the pointer lights up. Then the tape moves on by PITCH pixels, the roll turns by as much tape as came out, the knob scales to 0.88 and the machine jolts by a pixel.

The painted relief

Tape and labels are painted on canvas by the same function: the plastic with a gradient, the gloss over it and a thread of light at the top. Every letter is three passes: a soft shadow down and right, a light edge up and left and the letter itself whitened by how far it has been pressed (p from 0 to 1), with the die's dent while it comes down. The letters sit at a fixed pitch like on real tape, and on the canvas they use Archivo's width axis (fontStretch), or a squeeze where it is missing.

The corners that curl

Every corner has a size c and an angle a: the tape loses the triangle up to the fold and the tip, seen from above, comes back towards the fold by cos(a). Up to 90 degrees the front shows, catching the light, past it the back; under it, the shadow of the lifted corner. Once a label has landed its corners go from 0 to their resting value; the pointer lifts one a little more, ready to peel.

The flight and the ball

Cut, the label joins the row at once (the others make room with FLIP) and starts from where the tape was: it rises into the gap between the machine and the box, goes along and comes up into its place from below, so it never passes over another label. Peeled, it becomes a grid of triangles on a canvas over the page: every point goes from its place on the flat label to a point on a sphere crinkled by smooth noise, the grabbed corner first; every triangle is drawn with its own piece of the label (one affine transform per triangle), shaded by its normal and, where it has turned over, in the colour of the back.

Values to change

NameFileDefaultWhat it changes
STARTscript.js[['FRAGILE', 'red'], ['KITCHEN', 'blue'], ['BOOKS', 'black']]The labels on the box when the page opens, each with its tape: red, blue or black.
MAXscript.js6How many labels fit on the box.
LONGESTscript.js12The most letters a label can have (the groove is that long).
PITCHscript.js11.5The room every letter takes on the tape, in pixels.
PRESSscript.js0.42The seconds the machine takes for a letter: the wheel turns, the tape moves on, the knob comes down.
CURLscript.js6How far the corners of a label curl up, in pixels.
FLIGHTscript.js0.8The seconds a label takes to fly from the machine onto the box.
TOSSscript.js1How hard a crumpled label is thrown away (0 just drops it).

Try them in the Playground

When to use it

For the tags of a moving or storage app, the categories of an archive, the keywords of a handmade portfolio or blog, the tags of a vintage or stationery shop: wherever the tag field can be the piece people remember.

On phones

The machine shrinks to 82% and the groove sits beside it; the box has room for two rows, so a fourth label does not push the machine down. Tapping the bench opens the keyboard (the box is 16 pixels, so Safari on iPhone does not zoom, capitals are on and autocorrect off); the keyboard's key or the red lever cut. A tap on a label peels it off: instead of the pointer passing over, the corner lifts as it is tapped.

With reduced motion

The wheel goes straight to the letter and the tape comes out without sliding; the letters fade in, the label cut fades in at its place on the box and the one peeled fades away, with no flight and no ball; the roll is swapped with a fade and nothing shakes. The machine stays fully usable.

Pitfalls

  • The tape comes out to the left, not to the right: what comes out first goes furthest, and with the exit on the right the word would read backwards.
  • Typing faster than the machine: the text in the field is the truth and the machine follows it one job at a time, speeding up by how far behind it is. A comma arriving while the label before was still being cut joined the two words: the cuts waiting are in a queue, and the label being cut already counts for the limit and for duplicates.
  • The flight passed over the other labels or over the wheel's letters (text over text): now it rises into the gap between the machine and the box, goes along and comes up into its place from below, where there is nothing.
  • Canvas shadows (shadowOffset, shadowBlur) do not follow the transform: they have to be multiplied by devicePixelRatio, or on a retina screen they are half as big.
  • At first the ball was made of shards: random noise point by point tears the paper. With smooth noise neighbouring points fold together and make creases; the triangles are widened by half a pixel, or a line shows between them.
  • The folded corner vanished when the ball took the label's place: the ball's texture has to be painted with the corner as it is at that moment.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

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

Testing

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