Skip to content
animorio
IT
Back to the library

The bubblegum title

Text, HoverGSAPAdvancedCompressed weight: Demo 10 KBGSAP 28 KBFreeDownload the zip

A sweet shop's title made of pink bubblegum, puffy and glossy: under the pointer the letters stick and stretch into a strand that pulls and snaps, then spring back. Now and then a letter blows a bubble that grows, trembles and pops; a click pops a big one.

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 bubblegum title</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="shop">
    <header class="shop__top">
      <p class="shop__brand">Mollie’s Sweet Shop</p>
      <p class="shop__line">Pick and mix, weighed by hand</p>
    </header>

    <!-- the title is drawn in the svg below; this one is for screen readers and search engines -->
    <h1 class="shop__title" data-title>Chewy</h1>

    <!-- one svg as big as the window: the gum (letters, strands, shreds) goes through a single filter that melts it
         together and lights it; the bubbles sit on top, outside the filter, because they are thin and see-through -->
    <svg class="gum" data-gum aria-hidden="true">
      <defs>
        <filter id="gum-goo" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB" data-goo>
          <feGaussianBlur in="SourceGraphic" stdDeviation="6" result="blur" data-blur />
          <feColorMatrix in="blur" type="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 24 -11" result="goo" />
          <feGaussianBlur in="goo" stdDeviation="9" result="height" data-height />
          <feSpecularLighting in="height" surfaceScale="20" specularConstant="1" specularExponent="38" lighting-color="#ffffff" result="spec" data-spec>
            <feDistantLight azimuth="225" elevation="52" />
          </feSpecularLighting>
          <feComposite in="spec" in2="goo" operator="in" result="shine" />
          <feDiffuseLighting in="height" surfaceScale="20" diffuseConstant="1.18" lighting-color="#ffffff" result="light" data-diffuse>
            <feDistantLight azimuth="225" elevation="58" />
          </feDiffuseLighting>
          <feComposite in="goo" in2="light" operator="arithmetic" k1="0.6" k2="0.45" k3="0" k4="0" result="shaded" />
          <feComposite in="shine" in2="shaded" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="lit" />
          <feGaussianBlur in="goo" stdDeviation="10" result="soft" data-shadow-blur />
          <feOffset in="soft" dx="0" dy="12" result="drop" data-shadow-offset />
          <feFlood flood-color="#a3245a" flood-opacity="0.26" result="tone" data-shadow-tone />
          <feComposite in="tone" in2="drop" operator="in" result="shadow" />
          <feMerge>
            <feMergeNode in="shadow" />
            <feMergeNode in="lit" />
          </feMerge>
        </filter>
        <radialGradient id="gum-film" cx="0.42" cy="0.38" r="0.66">
          <stop offset="0" class="film-in" />
          <stop offset="0.72" class="film-mid" />
          <stop offset="1" class="film-out" />
        </radialGradient>
      </defs>
      <g data-world>
        <g class="gum__body" filter="url(#gum-goo)" data-body></g>
        <g class="gum__bubbles" data-bubbles></g>
      </g>
    </svg>

    <p class="shop__hint" data-hint>Move over the letters to stretch the gum. Click to blow a big bubble.</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.

19 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 bubblegum title” from Animorio and fit it into my site

What it does

On a wall of pink and cream stripes, under the sign of a made-up sweet shop, Mollie’s Sweet Shop, the word Chewy drops from above one letter at a time and lands with a squash and a springy bounce. The letters look like real bubblegum: puffy, with rounded edges, a glossy highlight at the top left, a shadow below and, where they touch, they melt into each other a little. Move the pointer over a letter and the gum sticks to it: as you pull away a strand comes out of the letter, getting longer, thinner and sagging under its own weight, while the letter stretches towards the pointer. Past a certain point the strand snaps: it whips back in, the letter springs home with an elastic that squashes and stretches it a couple of times, its neighbours tip a touch and a drop of gum falls away. Every few seconds a letter blows a bubble: a see-through pink film that swells in three breaths, with its highlights, then trembles all over and pops. In its place lie flaps of limp gum, which pull in and flop down over the letter, a few splashes and a little jolt; then the flaps are sucked back into the letter, which settles. A click quickly blows a big bubble on the nearest letter, which pops harder and makes the whole word jump; clicking on a bubble pops it at once.

How it works

Gum with one filter

The letters are plain SVG text, one per element, with an outline in the same colour that fattens them. Everything that is gum (letters, strands, flaps, drops) sits in a group with a single filter. A feGaussianBlur and a feColorMatrix that multiplies the alpha by 24 and takes away 11 make the goo effect: nearby pieces melt together and corners round off. Then the same result, blurred, becomes a height map: feSpecularLighting gives the glossy highlight, feDiffuseLighting the shade on the sides, and a feComposite arithmetic mixes them with the pink. Below, the same shape blurred and moved down makes the shadow.

The sizes

The word is measured on a canvas in the same typeface (measureText, with actualBoundingBox for each letter's true outline) and the size is chosen so that it fits the window. Everything else is counted in font sizes: the filter's blur, the relief, the thickness of strands and flaps, the reach, the bubbles. The SVG's viewBox is as big as the window, so one unit is one pixel and the filter's numbers stay right.

The strand

Each letter has a state (shift, stretch along a direction, squash on the baseline, tilt) that becomes a matrix, written as a transform. When the pointer enters a letter the hook point is fixed; on every frame the strand is drawn as a polygon along a quadratic curve, from the hook (moved by the same matrix) to the pointer: wide at the base, a waist that thins with the length, a blob on the tip, and the control point lowered by the square of the tension, so that it sags. Meanwhile the letter stretches towards the pointer.

The snap

Past its reach the strand snaps: it becomes a loose strand that goes back into the letter in 0.34 seconds with power3.in, while the letter springs back with elastic.out (the stretch dips below zero and the letter squashes sideways before it stops). A drop stays where the pointer was and falls with gravity, shrinking: through the filter's threshold it seems to soak away. The two neighbouring letters rock a little, and the letter stays free for half a second before it can stick again.

The bubble and the pop

The bubble sits outside the filter, on top of the letters: a circle with a see-through pink radial gradient, a thin rim and two white arcs of highlight; a neck of gum, inside the filter, ties it to the letter. A timeline blows it in three breaths, then makes it tremble with two sines at different speeds on scaleX and scaleY. When it pops the bubble disappears and the flaps start in its place: curved strokes inside the filter, the root near the middle and the loose edge pulling in and then falling. A moment later they are sucked into the middle of the letter, which squashes again. Bubbles come by themselves with a random delayedCall; a click blows a big one on the nearest letter, or pops the one it touches.

Values to change

NameFileDefaultWhat it changes
TEXTscript.js'Chewy'The title, one word: it sizes itself to fit the window.
REACHscript.js2.2How far a strand stretches before it snaps, in letter heights.
SPRINGscript.js0.3How the letters spring back: lower, and they wobble more before stopping.
PUFFscript.js1How puffed up the letters are: higher, and they get fatter and melt together more.
GLOSSscript.js1How shiny the gum is (0 = matt).
BUBBLEscript.js1How big the bubbles grow.
EVERYscript.js4.5How many seconds, more or less, between one bubble and the next.

Try them in the Playground

When to use it

For a title that begs to be touched: the site of a sweet shop or an ice-cream parlour, a gum or soft-drink brand, a kids' party page, a game, a summer event. It works with a short, big word, on its own in the middle.

On phones

The same animation with the sizes adapted: the word is nearly as wide as the screen and everything (strands, bubbles, flaps) is counted from its size. Drag a letter with a finger to stretch it; a tap blows the big bubble. Page scrolling is off, so the finger pulls the gum.

With reduced motion

Nothing moves: the word fades in, already in place, the letters do not stretch and the bubbles fade in and out, already blown, without popping. A click brings the big bubble in the same way.

Pitfalls

  • With tight, puffy letters the filter melted them into one lump and closed the eye of the e: the letters need a little space (0.035 of the size) and a thin outline, since the filter's threshold already does the puffing.
  • Specular light on a too-smooth height map drew steps, like the contour lines of a map: less blur and less relief give a steeper slope and the steps disappear. Flat areas also caught a whitish sheen: a higher exponent (38) keeps it on the edges.
  • Multiplying the pink by the diffuse light made the shaded sides nearly black: the feComposite arithmetic keeps half of the plain colour (k1 0.6, k2 0.45).
  • A strand thinner than the filter's blur vanishes under the threshold before it snaps: its waist never goes below 0.8 times the blur.
  • The bubble's flaps, starting at the rim and pointing to the centre, looked like spider legs: they start at the centre and the loose edge pulls in towards them, then drops.
  • The preview pilot sends a pointerleave that bubbles up to the root every time it changes element: the pointer counts as gone only when the pointerleave comes from the page root itself.
  • The filter over the whole window recomputed blurs and lights on millions of pixels every frame: its region shrinks to the rectangle where there is gum (the word, the strand, the flaps, the drops), rounded to 32 pixels so that it does not change every frame; and attributes are rewritten only when they change, so a still title costs nothing.

Libraries

GSAP 3.15.0, Fredoka (font variabile) 5.3.0.

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