Skip to content
animorio
IT
Back to the library

The wax word that melts

Text, HoverGSAP, CSS onlyMediumCompressed weight: Demo 10 KBGSAP 28 KBFreeDownload the zip

A minimal-style animated headline: a big word of ivory wax on warm white, with the volume, gloss and soft shadow of real wax. A small flame follows the pointer or the finger: near it the letters warm up, soften and slump, and slow drips run from their lower edges, stretch, let go and spread into pools on the ledge. Away from the flame the wax sets and slowly returns into the word.

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>Ember</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- a big word made of ivory wax on warm white: near the flame the letters soften, slump and drip slow drops that
       pinch off and pool on the ledge below; away from the flame the wax sets again and keeps its drips -->
  <main class="pagina" data-pagina>
    <header class="testa">
      <p class="marchio" data-entra>Ember</p>
      <p class="stato" data-entra><span class="stato__nome">Wax</span> <span class="stato__valore" data-stato>set</span></p>
    </header>

    <h1 class="parola" data-parola>Ember</h1>

    <svg class="scena" viewBox="0 0 1000 600" aria-hidden="true" data-scena>
      <defs>
        <!-- the wax: the drips melt into the letters (blur and alpha threshold), then a soft volume, a gloss that
             follows the flame and a warm shadow on the paper -->
        <filter id="cera" filterUnits="userSpaceOnUse" x="0" y="0" width="1000" height="600" color-interpolation-filters="sRGB">
          <feGaussianBlur in="SourceGraphic" stdDeviation="2.2" result="sfocato" />
          <feColorMatrix in="sfocato" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -8" result="forma" />
          <feGaussianBlur in="forma" stdDeviation="5" result="rilievo" />
          <feDiffuseLighting in="rilievo" surfaceScale="3" diffuseConstant="1" lighting-color="#fff7ea" result="ombreggiato">
            <feDistantLight azimuth="250" elevation="58" />
          </feDiffuseLighting>
          <feComposite in="forma" in2="ombreggiato" operator="arithmetic" k1="1.1" k2="0" k3="0" k4="0.035" result="volume" />
          <feSpecularLighting in="rilievo" surfaceScale="5" specularConstant="0.9" specularExponent="20" lighting-color="#fff4de" result="lucido">
            <fePointLight x="500" y="120" z="170" data-luce />
          </feSpecularLighting>
          <feComposite in="lucido" in2="forma" operator="in" result="lucidoDentro" />
          <feComposite in="volume" in2="lucidoDentro" operator="arithmetic" k1="0" k2="1" k3="0.55" k4="0" result="cera" />
          <feComposite in="cera" in2="forma" operator="in" result="ceraPulita" />
          <feGaussianBlur in="forma" stdDeviation="9" result="ombraMolle" />
          <feOffset in="ombraMolle" dx="0" dy="9" result="ombraGiu" />
          <feFlood flood-color="#6e5334" flood-opacity="0.2" result="tinta" />
          <feComposite in="tinta" in2="ombraGiu" operator="in" result="ombra" />
          <feMerge>
            <feMergeNode in="ombra" />
            <feMergeNode in="ceraPulita" />
          </feMerge>
        </filter>
        <linearGradient id="fiamma" x1="0" y1="1" x2="0" y2="0">
          <stop offset="0" stop-color="#fff6dc" />
          <stop offset="0.38" stop-color="#ffd27a" />
          <stop offset="0.78" stop-color="#f29a3c" />
          <stop offset="1" stop-color="#e2672a" />
        </linearGradient>
        <radialGradient id="alone">
          <stop offset="0" stop-color="#ffb860" stop-opacity="0.42" />
          <stop offset="0.45" stop-color="#ffb860" stop-opacity="0.12" />
          <stop offset="1" stop-color="#ffb860" stop-opacity="0" />
        </radialGradient>
      </defs>

      <path class="mensola" data-mensola d="M80 500 H920" />
      <g filter="url(#cera)">
        <g data-lettere></g>
        <g data-colate></g>
      </g>
      <g class="fiamma" data-fiamma>
        <circle r="120" fill="url(#alone)" data-alone />
        <path fill="url(#fiamma)" data-lingua />
        <path class="fiamma__cuore" data-cuore />
      </g>
    </svg>

    <footer class="piede">
      <p class="nota" data-entra data-nota>Bring the flame close to melt the wax</p>
      <p class="nota nota--destra" data-entra>Hand-poured candles</p>
    </footer>
  </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.

23 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 wax word that melts” from Animorio and fit it into my site

What it does

Ember is an invented studio of hand-poured candles. On the warm white of the page sits its name, large, in Fraunces, made of ivory wax: it has the soft volume of wax, a fine gloss and a warm shadow on the paper. The letters arrive one after another, as if poured from above, and settle with one unit too many and the return; then a small flame lights up and follows the pointer. Where it passes the wax slowly warms: the letter takes on a warmer tone, grows softer (the font’s SOFT axis rounds its corners), fuller, and slumps slightly on the line. Drips start from its lower edge: a bead that bulges, then a thread running slowly down, faster the longer and warmer it is, with the swollen drop at its tip. Sometimes the neck pinches and the drop lets go, falls, stretches as it falls and opens into a pool on the ledge, which bends by one unit under the hit. Move the flame away and the wax sets: the letters straighten with a small settle, the drips stop where they are and, after a few seconds in the cold, slowly return into the word, so the wax never piles up. Top right, the state of the wax: set, softening, melting. With no pointer, a flame passes along the word on its own.

How it works

The word measured in a hidden canvas

The word is written in a canvas as large as the drawing (1000 by 600 units), one letter at a time, at the size that makes it 800 wide. For each column we look for the lowest point with wax at least 10 units thick: a drip can start there. The SVG letters are placed in the same spots, so drips start right from their edge.

A single filter for the wax

Letters, drips, drops and pools sit in an SVG group with one filter: a blur and an alpha threshold weld them into a single mass (the drip’s neck blends into the letter by itself), then the same blurred shape is the height map for a diffuse light, which gives the volume, and for a point specular light, which is the gloss and sits on the flame. Below, a warm shadow, blurred and moved down.

The heat

Each letter has its own heat, which moves towards how close the flame is: it rises slowly and falls even more slowly. The heat drives Fraunces’ SOFT axis and weight, the tint from ivory to light amber and a spring that slumps the letter on the line; when the wax cools the spring brings it back straight with a one-unit settle.

Drips and drops

Above a heat threshold, drips start at random in the columns near the flame, at least 22 units apart. Each runs down at a viscous speed that grows with heat and length; past its length the neck pinches in a third of a second and the drop falls with gravity, stretching, and becomes a pool on the ledge that spreads on a spring. The ledge bends where the drop lands and straightens again.

Back into the mould

A drip that has been cold for RECAST seconds slowly retracts into the letter, and old pools shrink too: the demo can run for hours without the wax piling up. There are at most MAX_DRIPS drips.

The flame

The flame is a path redrawn every frame: it flickers on its own, and its tip lags behind the movement on a spring. It has a pale core, a wide amber glow and carries the gloss light with it. With no real pointer for more than 2.2 seconds, a fake flame passing along the word takes over.

Values to change

NameFileDefaultWhat it changes
WORDscript.js'Ember'The wax word: short, it fits on one line.
WEIGHTscript.js600The weight of cold wax (Fraunces goes from 100 to 900).
SWELLscript.js60How much weight a warm letter gains.
HEAT_RADIUSscript.js200How far the flame’s heat reaches, in units of the 1000-wide drawing.
MELT_SPEEDscript.js1How fast the wax warms up and runs.
DRIP_RATEscript.js0.5How many drips per second a warm letter starts.
MAX_DRIPSscript.js12The most drips on the word at once.
DROP_CHANCEscript.js0.5How often a long drip lets its drop fall (0 never, 1 always).
FLAME_SIZEscript.js1The size of the flame.
GLOSSscript.js0.55How much the wax gloss shines.
RECASTscript.js6After how many seconds in the cold a drip returns into the letter (0 never).

Try them in the Playground

When to use it

As the opening of a candle studio, a perfumery or fragrance house, a candlelit restaurant, a chocolate or pastry shop (the wax becomes icing), an autumn event; anywhere a single word should feel made of a real material and answer the warmth of the hand.

On phones

The same with a finger: dragging, the flame follows the finger, and without a finger it passes along the word on its own. The word fills 92% of the width and the scene is also sized on the height, so it fits the box. Over the demo the finger moves the flame instead of scrolling the page.

With reduced motion

The wax word fades in already dripped, with five drips and a pool standing still; there is no flame, nothing moves and the state stays ‘set’.

Pitfalls

  • Fraunces at a small optical size (low opsz) is wider: the measuring canvas uses automatic optical sizing, so the SVG letters must use opsz 144 too, or they overlap and drips start from nothing.
  • The filter blur erases the thin strokes of the E at the large optical size: at 3.2 units the E broke apart, at 2.2 with a 22/-8 threshold it stays whole.
  • If drips never go back, after a minute the word is a curtain: drips that have been cold for a few seconds must slowly retract, and their number kept low.
  • The step of the loop must be capped at a thirtieth of a second: in a slow browser the wax runs more slowly instead of jumping.

Libraries

GSAP 3.15.0, Fraunces (font variabile) 5.3.0.

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

Testing

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