Skip to content
animorio
IT
Back to the library

The caterpillar-nibbled logo

Intro, SVG, TextGSAPAdvancedCompressed weight: Demo 23 KBGSAP 28 KBFreeDownload the zip

A logo preview told like a little nature film: the letters sprout like seedlings, a plump caterpillar comes down on a silk thread, walks along the top of the letters and nibbles scalloped bites out of them, tunnels through a letter, wraps itself in a cocoon and comes out as a butterfly that flies off with the dot of the i. In the Playground you type your own name or upload your own logo.

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 caterpillar-nibbled logo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fredoka@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="scena">
    <!-- the stage: the script measures it, writes the logo in it and walks the caterpillar along its top -->
    <svg class="palco" data-stage role="img" aria-label="Olivebud" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <!-- the bites: everything white stays, the black circles are the holes in the logo -->
        <mask id="nibble" maskUnits="userSpaceOnUse" data-nibble-mask>
          <rect fill="#fff" data-nibble-full />
          <g fill="#000" data-nibble></g>
        </mask>
        <!-- the new growth that fills the bites when the logo heals: only inside the holes -->
        <mask id="grow" maskUnits="userSpaceOnUse" data-grow-mask>
          <rect fill="#000" data-grow-full />
          <g fill="#fff" data-grow></g>
        </mask>
        <!-- inside the tunnel the caterpillar is hidden under the top of the letter, except in the holes -->
        <mask id="hide" maskUnits="userSpaceOnUse" data-hide-mask>
          <rect fill="#fff" data-hide-full />
          <path fill="#000" data-hide-shape />
          <g fill="#fff" data-hide-holes></g>
        </mask>
        <!-- the piece of logo the butterfly carries away -->
        <clipPath id="loot-cut" clipPathUnits="userSpaceOnUse" data-loot-cut></clipPath>
        <!-- the little leaf, eaten bite by bite (in the leaf's own units) -->
        <mask id="leaf-bites" maskUnits="userSpaceOnUse" x="-80" y="-140" width="160" height="170">
          <rect fill="#fff" x="-80" y="-140" width="160" height="170" />
          <g fill="#000">
            <circle cx="-18" cy="-36" r="0" data-leaf-bite />
            <circle cx="-10" cy="-76" r="0" data-leaf-bite />
            <circle cx="16" cy="-66" r="0" data-leaf-bite />
            <circle cx="12" cy="-22" r="0" data-leaf-bite />
            <circle cx="0" cy="-2" r="0" data-leaf-bite />
          </g>
        </mask>
        <!-- the eyes keep the irises inside them -->
        <clipPath id="eye-near"><circle cx="7" cy="-7" r="8.2" /></clipPath>
        <clipPath id="eye-far"><circle cx="18" cy="-6" r="6" /></clipPath>
        <!-- the dark tip of the butterfly's fore wing -->
        <clipPath id="fore-wing"><path d="M6 -2C22 -46 70 -88 96 -72C114 -60 102 -20 78 -4C56 10 24 8 6 -2Z" /></clipPath>

        <!-- one segment of the caterpillar, drawn once and copied by the script: centre 0 0, walking to the right,
             feet down. The feet are behind the body, so only their ends show -->
        <g data-seg-model>
          <path class="zampa zampa--lontana" d="M-10 10L-10 22Q-10 26.5 -5.5 26.5Q-1 26.5 -1 22L-1 10Z" data-foot-far />
          <path class="zampa" d="M2 10L2 23Q2 27.5 6.5 27.5Q11 27.5 11 23L11 10Z" data-foot-near />
          <circle class="bruco" r="20" />
          <path class="bruco-pancia" d="M-17.5 6A18.5 18.5 0 0 0 17.5 6Q0 12.5 -17.5 6Z" />
          <path class="bruco-macchia" d="M-9 -13C-6 -18 2 -18 4 -13C2 -9 -6 -9 -9 -13Z" />
          <path class="bruco-lucido" d="M-12 -8Q-8 -15 0 -16.5" />
        </g>

        <!-- one wing of the butterfly, the right one: the left one is the same, mirrored -->
        <g id="wing">
          <path class="ala ala--dietro" d="M6 4C40 0 88 28 80 62C74 88 40 92 22 72C12 60 6 34 6 4Z" />
          <path class="ala-macchia" d="M44 58C48 50 60 50 62 58C60 66 48 66 44 58Z" />
          <path class="ala" d="M6 -2C22 -46 70 -88 96 -72C114 -60 102 -20 78 -4C56 10 24 8 6 -2Z" />
          <g clip-path="url(#fore-wing)">
            <ellipse class="ala-punta" cx="100" cy="-66" rx="34" ry="30" />
          </g>
          <path class="ala-bordo" d="M6 -2C22 -46 70 -88 96 -72C114 -60 102 -20 78 -4C56 10 24 8 6 -2Z" />
          <ellipse class="ala-macchia" cx="62" cy="-34" rx="9" ry="7" />
          <circle class="ala-macchia" cx="90" cy="-58" r="4.5" />
          <circle class="ala-macchia" cx="98" cy="-44" r="3.5" />
          <path class="ala-vena" d="M10 -2Q46 -30 86 -64M12 0Q50 -10 90 -30M10 6Q40 30 66 70" />
        </g>
      </defs>

      <g data-world>
        <g id="logo" data-logo></g>
        <g mask="url(#grow)" data-regrow></g>

        <!-- the little leaf that opens on one of the letters: its base is 0 0 and it grows upwards -->
        <g data-leaf>
          <g data-leaf-grow>
            <g mask="url(#leaf-bites)">
              <path class="foglia-gambo" d="M0 6Q-1.5 -2 0 -10" />
              <path class="foglia foglia--scura" d="M0 -8C-26 -22 -26 -66 0 -92Z" data-leaf-left />
              <path class="foglia" d="M0 -8C26 -22 26 -66 0 -92Z" data-leaf-right />
              <path class="foglia-vena" d="M0 -10L0 -84M0 -30L-11 -42M0 -50L-11 -62M0 -30L11 -42M0 -50L11 -62M0 -68L7 -76M0 -68L-7 -76" />
            </g>
          </g>
        </g>

        <!-- the silk thread it comes down on -->
        <line class="seta-filo" data-thread x1="0" y1="0" x2="0" y2="0" />

        <!-- the caterpillar: the script copies the segments in here, behind the head -->
        <g mask="url(#hide)" data-bug>
          <!-- the head: centre 0 0, looking to the right. It turns on its neck -->
          <g data-head-place>
            <g data-head-turn>
              <g data-ant-far>
                <g data-ant-far-wobble>
                  <path class="antenna antenna--lontana" d="M-6 -19Q-12 -33 -7 -45" />
                  <circle class="pallina pallina--lontana" cx="-7" cy="-46" r="4.6" />
                </g>
              </g>
              <g data-ant-near>
                <g data-ant-near-wobble>
                  <path class="antenna" d="M5 -21Q4 -36 12 -46" />
                  <circle class="pallina" cx="13" cy="-47" r="5.2" />
                </g>
              </g>
              <!-- the cheek that swells with food: behind the head, so that it bulges out of its outline -->
              <circle class="bruco" cx="12" cy="10" r="0" data-puff />
              <circle class="bruco" r="23" />
              <path class="bruco-lucido" d="M-15 -9Q-9 -18 1 -19.5" />
              <path class="bruco-macchia" d="M-15 3C-13 -1 -7 -1 -6 3C-7 7 -13 7 -15 3Z" />
              <ellipse class="guancia" cx="6" cy="8" rx="5.6" ry="3.4" data-cheek />
              <g data-mouth-box>
                <ellipse class="bocca" cx="18" cy="12.5" rx="4.6" ry="3.8" data-mouth />
                <path class="sorriso" d="M13.5 11.5Q18 15.5 22.5 11" data-smile />
              </g>
              <!-- the far eye, half behind the near one -->
              <circle class="occhio" cx="18" cy="-6" r="6.4" />
              <g clip-path="url(#eye-far)">
                <g data-pupil-far>
                  <circle class="iride" cx="20" cy="-5.5" r="4" />
                  <circle class="pupilla" cx="20.5" cy="-5.5" r="2.2" />
                  <circle class="riflesso" cx="19" cy="-7.2" r="1.3" />
                </g>
              </g>
              <ellipse class="palpebra" cx="18" cy="-6" rx="7.4" ry="7.4" data-lid-far />
              <circle class="occhio" cx="7" cy="-7" r="8.6" />
              <g clip-path="url(#eye-near)">
                <g data-pupil>
                  <circle class="iride" cx="10" cy="-6.5" r="5.3" />
                  <circle class="pupilla" cx="10.6" cy="-6.5" r="2.9" />
                  <circle class="riflesso" cx="8.4" cy="-9" r="1.8" />
                </g>
              </g>
              <ellipse class="palpebra" cx="7" cy="-7" rx="9.6" ry="9.6" data-lid />
            </g>
          </g>
        </g>

        <g data-loot></g>

        <!-- the cocoon: it hangs from 0 0, under the last letter -->
        <g data-cocoon-place>
          <g data-cocoon-swing>
            <path class="seta-gambo" d="M0 0L0 9" />
            <g data-shell>
              <path class="bozzolo" d="M0 6C18 8 26 38 23 64C20 88 8 102 0 104C-8 102 -20 88 -23 64C-26 38 -18 8 0 6Z" />
              <path class="bozzolo-ombra" d="M8 12C20 22 22 50 19 70C16 88 8 98 2 101C10 90 14 70 14 52C14 34 12 20 8 12Z" />
              <path class="bozzolo-righe" d="M-18 24Q0 31 18 23M-22.5 44Q0 53 22.5 43M-23 64Q0 73 22.5 63M-18 84Q0 91 17 83" />
              <path class="bozzolo-lucido" d="M-13 22Q-19 44 -15 70" />
              <ellipse class="bozzolo-buco" cx="0" cy="91" rx="10.5" ry="4.5" data-opening />
              <path class="bozzolo" d="M-12.5 90Q0 95 12.5 90C10 97 5 102.5 0 104C-5 102.5 -10 97 -12.5 90Z" data-cap />
            </g>
            <polyline class="filo" data-wrap />
          </g>
        </g>

        <!-- the butterfly: centre 0 0, head up. The wings fold towards the body to beat -->
        <g data-fly-place>
          <g data-wing-left><use href="#wing" /></g>
          <g data-wing-right><use href="#wing" /></g>
          <path class="farfalla-antenna" d="M-4 -42Q-10 -60 -20 -66M4 -42Q10 -60 20 -66" />
          <circle class="farfalla-pallina" cx="-21" cy="-67" r="3.6" />
          <circle class="farfalla-pallina" cx="21" cy="-67" r="3.6" />
          <path class="farfalla-corpo" d="M0 -26C7 -26 8 -10 7 10C6 32 3 44 0 46C-3 44 -6 32 -7 10C-8 -10 -7 -26 0 -26Z" />
          <path class="farfalla-righe" d="M-6 0Q0 3 6 0M-6 12Q0 15 6 12M-5 24Q0 27 5 24M-4 35Q0 37 4 35" />
          <circle class="farfalla-corpo" cx="0" cy="-34" r="10" />
          <circle class="occhio occhio--farfalla" cx="-4.6" cy="-36" r="4" />
          <circle class="occhio occhio--farfalla" cx="4.6" cy="-36" r="4" />
          <circle class="pupilla" cx="-4" cy="-35.4" r="2.2" />
          <circle class="pupilla" cx="5.2" cy="-35.4" r="2.2" />
          <circle class="riflesso" cx="-5" cy="-36.8" r="0.9" />
          <circle class="riflesso" cx="4.2" cy="-36.8" r="0.9" />
        </g>

        <g data-crumbs></g>
        <g data-fx></g>
      </g>
    </svg>
    <p class="nota">Click or tap to see it again</p>
  </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.

35 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 caterpillar-nibbled logo” from Animorio and fit it into my site

What it does

On cream paper the letters of a made-up brand, Olivebud, grow in a round, heavy, leaf-green typeface: they sprout from below one after the other, swaying like seedlings with a soft bounce, and two tiny leaves pop off each of them; on the l a small leaf opens, one half and then the other. A plump green caterpillar in segments comes down from above, head first on a silk thread: it touches the O, squashes like rubber, its antennae with orange balls at the tips go boing and the thread pulls back up. It looks around, blinks its big eyes and starts walking along the top of the letters, following their outline: it climbs the tall stems, drops into the valleys between one letter and the next, and as it walks a wave runs along its body from the tail to the head, one segment at a time lifts and the little feet under every segment take a step. First it eats the leaf, in four bites from the side towards the tip, with green crumbs flying. Then it stops several times on the top edge of the letters and nibbles: at each bite its mouth opens and shuts, its cheek swells, one more small circle appears in the edge (three per bite, like a scallop) and a shower of crumbs falls; the mouthful runs down its body as a bulge and after every meal the caterpillar gets fatter with a spring. In front of the b it narrows its eyes, wiggles and dives in: it disappears under the surface of the letter, the letter swells and trembles, and the caterpillar pops out on the other side in a burst of crumbs, leaving two round holes. At the end it goes over the last letter, climbs down its side and hangs under it in a J, swinging. It turns its head, a thread wraps it from the top down and the cocoon closes; the cocoon shakes twice, its tip pops off and out comes an orange butterfly with its wings still folded, which spring open and start beating. The butterfly flies a figure of eight round the logo, swoops down on the i and carries off its dot. The logo stays like that, scalloped and holed, with the empty cocoon swinging and the butterfly passing by with its prize; then it heals: the butterfly brings the dot back and drops it into place with a bounce, new green grows in the bites with tiny leaves popping out of it, the green ripens into the colour of the logo, the leaf on the l opens again and the story starts over. A click restarts it from the beginning. A picture can take the place of the word: the caterpillar follows its outline, bites it and tunnels through it there too.

How it works

The outline of the letters

The stage is an SVG whose viewBox is as big as the window, so one unit is one pixel. The letters are drawn on a canvas in the same typeface and the pixels are read column by column: for each column, the highest and the lowest point of the ink. That gives the edge to bite, the dot of the i (the column at the middle of the letter: full, empty, full) and the point under the last letter where the caterpillar hangs. A picture from another site cannot be read (getImageData throws) and then it counts as a rectangle.

The caterpillar’s road

The path is the centre of a ball as big as the caterpillar rolled along the top edge: for each x, the highest point among all the nearby columns minus the distance to the ball. So the caterpillar climbs the stems, drops into the valleys and goes round the corners at the right distance; where the ball falls into nothing (a space in the name) there is a little hop. The path is resampled at a fixed step and smoothed, so a distance along the road is just an index. The head sits at a distance u, every segment a little further back: the timeline only moves the number u.

The segmented caterpillar

One segment is drawn once in the page (body, belly, spot, shine, two little feet) and copied six times by the script, from the tail to the neck, behind the head. On every frame render() puts each piece on the road with a transform attribute: place, direction and size. While it walks, a wave runs from the tail to the head (the sine of the distance walked plus a delay per segment) and lifts one segment at a time, closes the gap between neighbours and swings the little feet; the mouthful is a Gaussian sliding along the segments and the fattening is a number that widens them all with a spring. Head, antennae, eyelids, mouth and cheeks turn on their joints, worked out with getBBox.

The bites and the tunnel

The bites are an SVG mask on the letters: a white rectangle and black circles growing from nothing, three per bite, resting on the edge read from the canvas. In the tunnel the caterpillar goes along a U inside the letter; since it is fatter than the stems, it is not hidden by the letter’s shape but by a second mask on the caterpillar: a black polygon covering everything below the letter’s top edge, with two white circles where the letter is holed, so the head is seen going in and out of the holes.

Cocoon and butterfly

Once hanging, the caterpillar stops following the road and becomes a chain hanging from the last letter and bending into a J; a number from 0 to 1 blends one shape into the other. The thread that wraps it is a zigzag polyline drawn with stroke-dashoffset, while the shell grows from the top. The butterfly flies along Catmull-Rom curves (a figure of eight round the logo, then down to the i and away) and the wings beat by folding towards the body with scaleX. The dot of the i is a copy of the letter cut by a clipPath above the gap between dot and stem, while a black rectangle in the mask removes the real dot: a number carries it from the letter to the butterfly’s legs and, when healing, back down with bounce.out.

Direction and your logo

The whole story is one timeline, with timeScale for the speed; the caterpillar’s and the butterfly’s numbers are changed with T.set and T.to, never with callbacks, so the timeline can be moved back and forth. When it heals, a green copy of the bitten letters shows only inside circles growing in the holes, then the holes close underneath and the green fades; the story starts again from the «arrive» label. If IMAGE holds an address, the logo becomes that picture: the caterpillar follows its outline, bites it, tunnels through it near its end and the butterfly carries off a small round piece.

Values to change

NameFileDefaultWhat it changes
NAMEscript.js'Olivebud'The logo as a word: the caterpillar walks along the top of its letters. If there is an i (or a j), the butterfly carries off its dot.
IMAGEscript.js''The logo as a picture: the address of a file, or upload it from your computer. If there is one, it takes the place of the word.
BITESscript.js4How many times the caterpillar stops to nibble the edge of the logo, at most: on a short logo it only takes as many as fit.
TUNNELscript.js5The letter it tunnels through, counting from 0 (spaces do not count). If it is too narrow, the nearest wide enough one is used. A picture is tunnelled near its end.
GROWscript.js0.5How much fatter it gets by eating: 0.5 means half as fat again by the end, 0 keeps it as it is.
BUGscript.js1How big the caterpillar is next to the letters (and with it the cocoon and the butterfly).
SPEEDscript.js1The speed of the whole story: 2 is twice as fast, 0.5 half as fast.
RESTscript.js4How many seconds the logo stays nibbled, with the empty cocoon swinging, before it heals.
LOOPscript.jstrueWhether the logo heals and the story starts again. With false it stays nibbled, the cocoon keeps swinging and the butterfly passes by every now and then.

Try them in the Playground

When to use it

To present a logo with a smile: a preview for a client, the opening of a site for a brand of natural things (gardens, herbal teas, gardening, children’s products), a friendly 404 or loading page, a social post. It works with a written wordmark and with a picture.

On phones

The same story, with the sizes adapted: the letters take up almost the whole width, the caterpillar, the cocoon and the butterfly are a little bigger next to the letters, and the point where the caterpillar hangs always stays inside the screen, so the cocoon and the butterfly never go past the edge. A tap restarts it.

With reduced motion

Nothing moves: the logo fades in, then the caterpillar resting on the letters; then the bites and the holes fade in (and the leaf goes), the caterpillar gives way to the cocoon and the butterfly appears with the dot of the i. If the story starts again, the healing is a fade too.

Pitfalls

  • Coming down head first on the thread, the antennae bent backwards lay across the segments of the body: on the way down they bend forwards, away from the body, and spring back when it lands.
  • Every bite is three small circles resting on the edge read column by column: over the u one circle fell into the gap between the two arms (there the highest edge is the bottom of the letter) and holed the u in the middle. A circle that would sit far below the middle one is skipped.
  • The first bite came right after landing, while the body was still straight on the thread: bites stay at least three and a half caterpillar sizes away from where it lands.
  • On a phone the cocoon and the butterfly coming out of it ended up past the right edge: the point where the caterpillar hangs moves inwards as much as needed, even if it ends up under the letter before.
  • The piece of logo the butterfly carries, held just under its body, disappeared behind the hind wings: now it hangs lower, below the tail.
  • In a picture the column chosen for the piece to carry off fell into the gap between two shapes (and there was no piece): the nearest full column is used.
  • With a long name the walk took almost a minute: the speed grows with the length of the road, so the walk lasts six and a half seconds at most.

Libraries

GSAP 3.15.0, Fredoka (font variabile) 5.3.0.

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

Testing

Tested on 10 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 animations