
Words that melt into each other
Text, Scroll, SVG
NewFree
A green chalkboard in a wooden frame: a stick of chalk writes the title in joined-up handwriting, with a grainy line and dust falling, wears down, snaps in two and one piece bounces on the tray; under it, it draws a sketch. Then the eraser passes, leaves a white haze and the next phrase is written. A click sends the eraser at once.
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 title written in chalk</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/familjen-grotesk@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="scena" data-scene>
<!-- the phrase on the board, for screen readers: the script keeps it in step with what the chalk writes -->
<h1 class="leggibile" data-reader>Fresh coffee all day</h1>
<!-- the slate: its texture, the chalk, the dust and the white haze the eraser leaves are painted on this canvas -->
<canvas class="lavagna" data-board aria-hidden="true"></canvas>
<!-- the wooden frame, the tray, the stick of chalk, its broken pieces and the eraser: the script sizes them -->
<svg class="cornice" data-stage aria-hidden="true">
<defs>
<filter id="grana-orizzontale" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
<feTurbulence type="fractalNoise" baseFrequency="0.006 0.22" numOctaves="3" seed="4" result="rumore" />
<feColorMatrix in="rumore" type="matrix" values="0 0 0 0 0.24 0 0 0 0 0.13 0 0 0 0 0.06 0 0 0 1.5 -0.55" result="venature" />
<feComposite in="venature" in2="SourceGraphic" operator="atop" />
</filter>
<filter id="grana-verticale" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
<feTurbulence type="fractalNoise" baseFrequency="0.22 0.006" numOctaves="3" seed="9" result="rumore" />
<feColorMatrix in="rumore" type="matrix" values="0 0 0 0 0.24 0 0 0 0 0.13 0 0 0 0 0.06 0 0 0 1.5 -0.55" result="venature" />
<feComposite in="venature" in2="SourceGraphic" operator="atop" />
</filter>
<filter id="polvere-sul-gesso" x="-0.1" y="-0.3" width="1.2" height="1.6" color-interpolation-filters="sRGB">
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="1" seed="2" result="rumore" />
<feColorMatrix in="rumore" type="matrix" values="0 0 0 0 0.55 0 0 0 0 0.53 0 0 0 0 0.48 0 0 0 2.2 -1.25" result="puntini" />
<feComposite in="puntini" in2="SourceGraphic" operator="atop" />
</filter>
<filter id="ombra-morbida" x="-0.5" y="-0.5" width="2" height="2">
<feGaussianBlur stdDeviation="2.6" />
</filter>
<linearGradient id="gesso" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" style="stop-color: var(--chalk-light)" />
<stop offset="0.45" style="stop-color: var(--chalk)" />
<stop offset="1" style="stop-color: var(--chalk-shade)" />
</linearGradient>
<linearGradient id="legno-cornice" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" style="stop-color: var(--wood-light)" />
<stop offset="0.5" style="stop-color: var(--wood)" />
<stop offset="1" style="stop-color: var(--wood-dark)" />
</linearGradient>
<linearGradient id="legno-lato" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" style="stop-color: var(--wood-light)" />
<stop offset="0.55" style="stop-color: var(--wood)" />
<stop offset="1" style="stop-color: var(--wood-dark)" />
</linearGradient>
<linearGradient id="legno-cancellino" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" style="stop-color: var(--eraser-light)" />
<stop offset="0.6" style="stop-color: var(--eraser)" />
<stop offset="1" style="stop-color: var(--eraser-dark)" />
</linearGradient>
</defs>
<g data-tray-back></g>
<g data-objects></g>
<g data-front></g>
</svg>
<p class="nota" data-hint>Click or tap to wipe the board</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>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.
26 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “The title written in chalk” from Animorio and fit it into my siteThe letters are the Hershey Script 1-stroke alphabet (from the early sixties, free to use with its note of origin), written in the script as numbers: for every letter its width and its strokes, points joined by lines, with the baseline at zero and small letters 9 units tall. Joined-up letters end where the next one begins, so strokes that touch are merged into one: a word comes out without lifting the chalk. The dot of the i and the bar of the t move to the end of the word. Every stroke goes through Chaikin's algorithm three times, which rounds off the corners, and keeps its running lengths, so the tip's position is known at every moment. The phrase wraps onto one, two, three or four lines: whichever makes it biggest wins.
The chalk is laid down in dabs on a canvas: every few pixels of path, three small semi-transparent circles moved at random, smaller where the stroke starts and ends, with a pressure that sways slowly. Every frame this canvas is copied onto another and passed through destination-in over a noise pattern (a 160 pixel tile with fuller and emptier patches and 16% holes): those are the pores of the slate, fixed to the board, where the chalk skips. The dust is little specks with gravity born from the tip as it writes.
The stick is an SVG path built from its length, with a round or a broken tip (a random zigzag) and a gradient from one side to the other; its sway is the writing angle plus a damped lean that follows its speed. It gets shorter by a little for every pixel written. At the breaking point a timeline brings up the crack and makes it tremble, then snaps it: the front half becomes a piece of its own, which falls through a small simulation (gravity, bounces that lose 64%, a turn until it lies flat) worked out once and played back by a tween, so GSAP keeps the time; the half that stays moves by half its length and looks exactly as before, so there is no jump.
When the eraser starts, a copy of the writing is shrunk to a sixth: scaled up again it is a blurred writing. The eraser goes in rows with sine.inOut tweens and every frame, in short steps along its path, takes the chalk away with destination-out and, clipped to its outline, lays on the haze canvas a veil of that blurred copy, shifted a little backwards, plus the fine lines of the felt. Each pass takes away half of the chalk: after two or three only a trace is left, as it really happens. In the tray the eraser is seen side on; to turn to the board it squashes in height and comes back showing its back.
Every round is a single timeline: the chalk lifts, writes stroke by stroke (one tween per stroke, its length giving its duration, and a hop for every lift), goes back to the tray, waits, the eraser passes; the same for every phrase. At the end the eraser pushes the pieces out of the tray, the haze and the leftover chalk fade, and at the end of the timeline the round starts again, identical, with the same random numbers. A click stops the timeline and builds a new one starting with the eraser. The frame and the tray are SVG rectangles with a stretched feTurbulence for the wood grain; the tray's front lip sits above the objects, so whatever falls lands inside it.
| Name | File | Default | What it changes |
|---|---|---|---|
PHRASES | script.js | ['Fresh coffee all day', 'Lemon cake, baked today'] | The phrases written on the board, one after another: the eraser wipes each one and the chalk writes the next. Letters, numbers and the signs . , ' ! ? : - & / ( ). |
SKETCH | script.js | true | The sketch under every phrase: a cup of coffee, then a lemon (with false only the phrase stays, bigger). |
WRITE_SPEED | script.js | 1 | How fast the chalk writes: 2 is twice as fast, 0.5 half as fast. |
BREAK | script.js | true | Whether the chalk snaps halfway through the first phrase. |
DUST | script.js | 1 | How much chalk dust falls from the tip and puffs from the eraser (0 = none). |
HAZE | script.js | 1 | How much white haze the eraser leaves (0 = a clean board, 2 = the writing stays easy to read under the new one). |
REST | script.js | 1.8 | How many seconds the finished phrase stays on the board before the eraser comes. |
To open the page of a bar, a café, a bakery or a restaurant with its daily menu, a school, a course or a bookshop; for a message that changes, like the dish of the day or the opening hours; for a post or a story with a handmade touch.
The same animation with the sizes adapted: the phrase wraps onto two or three lines to stay big, the sketch gets smaller, and the chalk, the eraser and the tray are sized on the short side of the screen. A tap sends the eraser.
Nothing moves: the chalk and the eraser stay in the tray, the phrase and the sketch fade in already written, then fade out leaving the haze and the next phrase fades in; at the end the haze fades too.
GSAP 3.15.0, Familjen Grotesk (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 20 KB; the libraries: GSAP 28 KB.
Tested on 10 October 2026
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.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.