
The copy button
Buttons, Text, SVG, Physics
NewFree
A podcast's Subscribe button made as the head of an old ribbon microphone lying on its side: a chrome body, a grille of fine bars and behind it the pleated aluminium ribbon. Pressed, the word is spoken into the microphone: the sound runs along the ribbon, which draws the word's own waveform, and shakes the bars one by one; the letters swell as they are said, the ON AIR lamp warms up and lights, and the d of Subscribed grows in right when it is heard.
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 ribbon microphone button</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/anybody@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the Subscribe button of a podcast, made as the head of an old ribbon microphone lying on its side: a chrome
body, a grille of fine chrome bars and, behind them, the corrugated ribbon. Pressed, the word is spoken into
it: the sound runs out along the ribbon and shakes the bars, the letters swell as they are said, the ON AIR
lamp lights and the missing letters of Subscribed grow in -->
<div class="mic-wrap" data-wrap>
<button class="mic" type="button" aria-pressed="false" aria-label="Subscribe to the podcast" data-mic data-autoclic>
<canvas class="mic__grille" aria-hidden="true" data-grille></canvas>
<span class="mic__shade" aria-hidden="true"></span>
<span class="mic__lamp" aria-hidden="true">
<span class="mic__glass mic__glass--warm" data-warm></span>
<span class="mic__glass mic__glass--lit" data-lit></span>
<span class="mic__onair mic__onair--dim" data-dim-text>ON AIR</span>
<span class="mic__onair mic__onair--lit" data-lit-text></span>
</span>
<span class="mic__plate" aria-hidden="true" data-plate>
<span class="mic__word" data-word>Subscribe</span>
</span>
</button>
<p class="sr" aria-live="polite" data-live></p>
</div>
</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 ribbon microphone button” from Animorio and fit it into my siteThe waveform is not recorded: the spell() function builds it from the letters of DONE. Each vowel becomes a stretch of voiced wave with its own pitch (the a lowest, the i highest) and three harmonics; s, f, h, z, x and a soft c a hiss of smooth noise (a hash at whole steps, joined with smoothstep); b, d, g, k, p, t and a hard c a moment of silence and then a snap that dies fast; l, m, n, v, w a softer vowel and the r rolled, its loudness trembling 26 times a second. A silent e after a consonant is short and soft. Each stretch has its envelope, and sig(voice, t) gives the wave's value at any moment.
The ribbon is drawn on a canvas one column of pixels at a time. The column at a distance d from the plate shows the voice as it was d / SPEED seconds before: so the word comes out from under the plate and runs to both ends, and reads on the ribbon as on an oscilloscope. The pleats are alternate light and dark columns, and the slope of the wave lightens or darkens them like slats tilting towards the light; the columns end up in nine Path2D paths, one per tone, filled one at a time.
Every bar of the grille is a string held at both ends, drawn with a quadratic curve: the middle point is its displacement. The displacement is a damped harmonic oscillator (frequency RING, damping DAMP) driven by the same wave that reaches the ribbon under it, stepped in 1/240 of a second with the frame capped at 1/30. The bars are split into three paths by the way they bend, each with its own chrome gradient: those bent towards the light shine, the others go dull.
Over the bars, in «lighter» mode, the same path is stroked again with a white radial gradient centred on the pointer (the glint) and a red one centred on the lamp, as strong as the lamp is lit. The pointer passing over the bars plucks them: those between its last position and the new one get a speed in proportion to the pointer's speed.
The word on the plate is split into letters, each with the wght and wdth axes of the Anybody variable font in its CSS variables: every frame each letter takes its weight and width from the envelope of its stretch of voice. The letters Subscribed has in addition grow from zero width with back.out at the moment of their sound, and the plate is as wide as the longer word from the start, so it never jumps. The lamp is a GSAP timeline over two layers of glass: a first glow, a dip, then up to full in WARMUP seconds; going out, the red gives way to an amber glass that fades more slowly.
| Name | File | Default | What it changes |
|---|---|---|---|
DONE | script.js | Subscribed | The word once subscribed: the letters it shares with the plate stay, the others grow in as they are said. It is also the word the ribbon draws. |
TALK | script.js | 1 | How slowly the word is said: 1 = about a second, 2 = twice as long. |
SPEED | script.js | 230 | How fast the sound runs along the ribbon, in pixels per second. |
LOUD | script.js | 1 | How hard the voice moves the ribbon and shakes the bars: 0 not at all. |
BARS | script.js | 3.2 | The spacing of the grille's bars, in pixels. |
RING | script.js | 9 | How many times a second a bar swings on its own. |
DAMP | script.js | 0.09 | How quickly a bar stops ringing: small rings long. |
PLUCK | script.js | 1 | How much a pointer drawn across the grille plucks the bars: 0 not at all. |
WARMUP | script.js | 0.22 | The seconds the ON AIR lamp takes to warm up and light fully. |
For subscribing to a podcast, an online radio, a live show or a channel of interviews: wherever the voice is the product. The big moment lasts a second and dies down on its own; once on, the ON AIR lamp tells at a glance that you are subscribed.
The pill is 60 pixels tall and 336 wide, or the screen less 16 pixels a side: at 390 pixels it fits whole. At a touch it gives and the word is spoken as with the mouse; the glint shows under the finger, and dragging the finger along the grille plucks the bars as the mouse does. A second tap unsubscribes.
Nothing moves: the ribbon and the bars stay still, the letters do not swell, the pill does not give. Pressing it, the ON AIR lamp lights with a fade and the d of Subscribed fades in; unsubscribing, the lamp goes out the same way.
GSAP 3.15.0, Anybody (font variabile, con gli assi del peso e della larghezza) 5.3.0.
Compressed with gzip, the three demo files weigh 11 KB; the libraries: GSAP 28 KB.
Tested on 13 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.