
Words that melt into each other
Text, Scroll, SVG
NewFree
A metronome drawn in thin lines, in the minimal style: the rod swings like a real pendulum and on every beat a syllable falls onto the line and takes on weight. The last syllable of the bar is the accent, in vermilion. You can drag the weight on the rod: higher is slower, lower is faster, and the BPM figure changes its width and weight with the tempo.
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>Metronome</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" data-stage aria-label="A metronome. On every beat a syllable falls onto the line; the last one of the bar is accented.">
<!-- the hairlines: the line the syllables land on and the metronome, drawn by the script -->
<svg class="scene" data-scene aria-hidden="true">
<path class="rule" data-rule d="" />
<g data-ticks></g>
<g data-metro>
<path class="floor" data-floor d="" />
<g data-swing>
<path class="arc" data-arc d="" />
<path class="mark" data-mark-l d="" />
<path class="mark" data-mark-r d="" />
<path class="flash" data-flash-l d="" />
<path class="flash" data-flash-r d="" />
</g>
<path class="body" data-body d="" />
<path class="scale" data-scale d="" />
<g data-rod>
<path class="rod" data-rod-line d="" />
<path class="weight" data-weight d="" />
</g>
<path class="pivot" data-pivot d="" />
</g>
</svg>
<!-- the syllables, one span each, built by the script -->
<div class="words" data-words aria-hidden="true"></div>
<span class="probe" data-probe aria-hidden="true"></span>
<!-- the handle on the metronome: drag it up and down, or use the arrow keys -->
<div class="grip" data-grip role="slider" tabindex="0" aria-label="Tempo" aria-valuemin="40" aria-valuemax="208" aria-valuenow="72" aria-valuetext="72 beats per minute"></div>
<p class="tempo" data-tempo aria-hidden="true">
<span class="tempo__num" data-bpm>72</span>
<span class="tempo__meta">
<span class="tempo__unit">BPM</span>
<span class="tempo__window"><span class="tempo__name" data-name>Adagio</span></span>
</span>
</p>
<p class="hint" data-hint aria-hidden="true">Drag the weight to change the tempo</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.
21 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 syllable metronome” from Animorio and fit it into my siteThe whole direction is one paused GSAP timeline whose time is not in seconds but in beats: syllable k of the word falls between beat b0 + k − 0.42 and b0 + k, the word closes up half a beat after the accent, the next one starts REST beats later. The timeline's timeScale is the tempo divided by 60: at 72 BPM a beat lasts 0.83 seconds, at 144 half that. When the timeScale changes GSAP keeps the playhead where it is, so the tempo can change at any moment without a jump.
The rod's angle is SWING · cos(π · t), with t the timeline's time in beats: on even beats the rod is all the way right, on odd beats all the way left, still for an instant at the ends and fast in the middle, like a real pendulum. The tick fires when t passes a whole number; the mark at that end of the arc lights up and fades. The rod is a curve that bends a pixel outwards at the ends, where the acceleration is highest, and more at fast tempos. So that the loop comes back identical, the whole cycle always has an even number of beats.
The weight's place on the rod is a number u between 0 (low, 208 BPM) and 1 (high, 40 BPM), and the tempo is 208 · (40 / 208)^u: a logarithmic scale, like the marks on a real metronome, closer together at fast tempos. When you drag, the pointer's vertical travel becomes u; the weight follows it with a small lag, so the tempo changes smoothly too. The arrow keys change the tempo by 2 BPM (10 with Shift).
Every syllable is a span inside another: the outer one moves and squashes, the inner one carries the wght and wdth axes of the Archivo variable font. It falls light (wght 150) and narrow (wdth down to 84); on the tick it jumps to wdth 118 and a height of 0.86, then goes back to 100 and 1 on an elastic spring, while its weight rises to 560 (860 for the accent). Each syllable's width is measured at its final weight, and the outer span has that width with the text centred, so changing the axes does not shift the word.
The line is an SVG path redrawn at every frame: when no syllable is landing it is a straight line, otherwise a point every 5 pixels with, under each syllable that has just landed, a Gaussian bell a couple of pixels deep that flattens out on the same spring. The metronome is drawn in its own units (the body is 100 tall) with vector-effect: non-scaling-stroke, so the lines stay thin at any screen size.
| Name | File | Default | What it changes |
|---|---|---|---|
WORDS | script.js | ['ca-den-za', 'syn-co-pa-tion', 'al-le-gro'] | The words, one per bar: the hyphens split the syllables, one falls on every beat and the last one is the accent (up to 6 words of 8 syllables). |
TEMPI | script.js | [72, 104, 144] | The tempo of each word in beats per minute, 40 to 208: the weight slides there by itself before the word starts (until someone drags it). |
SWING | script.js | 26 | How many degrees the rod swings to each side. |
REST | script.js | 4 | How many empty beats after the accent before the next word: in them the word closes up, stays to be read and leaves. |
LIGHT | script.js | 150 | The weight of a syllable while it falls (100 to 900). |
HEAVY | script.js | 560 | The weight it takes on when it lands (100 to 900). |
ACCENT | script.js | 860 | The weight of the accented syllable, the last of the bar (100 to 900). |
SPRING | script.js | 1 | How springy every landing is: 0.4 lands dead, 2 wobbles for longer. |
For a site that lives on rhythm: a music school, a festival, a practice app, a podcast, the title of a page about tempo and metre. Also as the header of a diction lesson or of a page about words broken into syllables. The metronome with the draggable weight is a real control too: it can be used to pick a value.
The same animation with the sizes adapted: the metronome stands at the top in the middle on its own base, the line with the syllables below it, nearly as wide as the screen, and the tempo under the line. You drag the weight with a finger, up and down on the metronome.
Nothing swings and nothing falls: the rod stays upright and on every beat the mark on one side and then the other fades in; the syllables fade in already in their place in the word and already at their weight, the accent in vermilion, and the word fades out. The weight can still be dragged, and it moves without sliding.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 10 KB; the libraries: GSAP 28 KB.
Tested on 16 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.