
The needle gauge
Counters, Physics, SVG
NewFree
A music player with a clear cassette seen close up, and no sound: the tape is the player. Smoked plastic with its reflections, screws, a label written by hand; as it plays the reels turn, the brown tape runs from one to the other and the reel that empties spins faster and faster, as the physics of the radii says. Forward and back wind the tape at full speed to the song, with the reels blurred by the motion. Turning a reel with a finger goes forward and back, and the tape goes slack in a loop that hangs and then pulls tight again.
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 cassette player</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/wdth.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/caveat@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the cassette player: a music player whose top is a clear smoked cassette seen close up, sitting on the deck.
The reels, the tape and the deck's head are painted on a canvas every frame; the shell, the label and the
screws are SVG. The packs of tape grow and shrink with the time, a reel turned by hand pulls the tape or lets
it go slack, and the keys go down like a deck's piano keys. No sound -->
<section class="player" aria-label="Cassette player" data-player>
<div class="deck" data-deck>
<svg class="slot" viewBox="0 0 352 252" aria-hidden="true" data-slot></svg>
<svg class="shell" viewBox="0 0 352 252" aria-hidden="true" data-back></svg>
<canvas class="inside" aria-hidden="true" data-inside></canvas>
<svg class="shell" viewBox="0 0 352 252" aria-hidden="true" data-front></svg>
<div class="grip" role="slider" tabindex="0" aria-label="Position on side A" aria-valuemin="0" data-grip></div>
</div>
<ol class="card" aria-label="Side A" data-list></ol>
<div class="times" aria-hidden="true"><span data-at>0:00</span><span data-left>0:00</span></div>
<div class="keys">
<button class="key" type="button" aria-label="Previous song" data-key="rew"><span class="key__body"><span class="key__cap"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M11.5 6.5v11L4 12zM20 6.5v11L12.5 12z"/></svg></span></span></button>
<button class="key key--play" type="button" aria-label="Play" aria-pressed="false" data-key="play" data-autoclic><span class="key__body"><span class="key__cap"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8 5.5v13l10.5-6.5z"/></svg></span></span></button>
<button class="key" type="button" aria-label="Pause" data-key="pause"><span class="key__body"><span class="key__cap"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 6h3.6v12H7zM13.4 6H17v12h-3.6z"/></svg></span></span></button>
<button class="key" type="button" aria-label="Next song" data-key="ff"><span class="key__body"><span class="key__cap"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12.5 6.5v11L20 12zM4 6.5v11L11.5 12z"/></svg></span></span></button>
</div>
<p class="sr" aria-live="polite" data-live></p>
</section>
</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.
34 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 cassette player” from Animorio and fit it into my siteEverything comes from one number, x, the seconds of tape gone past the head. The side is the clear leader, the songs with three seconds of silence between them and some blank tape at the end; the tape runs at 4.76 centimetres a second, which in the drawing is 167 units. The song's time, the song playing, the highlighter and where the splice between leader and brown tape is all come from x.
A pack is wound tape, so its area grows with its length: r² = r₀² + k·L, with r₀ the hub and k chosen so the whole tape makes a full pack of 79 units. The left pack holds the tape still to come, the right one the tape gone by. Since the tape goes past the head always at the same speed, each hub turns at v / r: at the start the empty one turns more than twice as fast as the full one, and by the end of the side they have swapped. The free tape runs along the outer tangents between the packs and the rollers, worked out again every frame.
Each free span, from a pack to its roller, knows how much more tape it has than the straight line. A reel that lets tape go adds it to its span; one that winds first takes the slack and then, with its span tight, pulls the tape past the head (x changes) and takes slack from the other span, which when it goes below zero drags the other reel. The reels have a speed of their own that friction kills in COAST seconds: when the pull stops short they keep turning and make a loop. Then the clutch winds it in over TENSION seconds.
A slack span is a Verlet chain of 26 points between its two ends, as long as the straight line plus the slack: weight, 16 passes of length constraints, two stiffness constraints (two and three links apart never get closer than a smallest curl: the tape curls, it does not zigzag), and collisions with the packs, the rollers and the edges of the plastic. When the slack dies away the tension pulls it straight quickly and the chain rests. It is drawn as a Catmull-Rom curve.
The hub with its teeth, the tape clamp and the deck's spindle are drawn once and turned; the light on the hub and on the pack does not turn. In the fast wind a hub can turn twice in one frame and at 60 frames a second it would look still or backwards: it is drawn several times across the angle it turned in that frame, with opacity 1, 1/2, 1/3… which gives the right average, that is real motion blur.
The keys are real buttons with a CSS variable --press that GSAP takes from 0 to 1: the cap goes down and its front face gets shorter; play stays down at 0.8 with aria-pressed, forward and back stay down until the tape gets there and come back up with an elastic bounce. The head and the roller rise in 0.24 seconds, then the capstan picks up speed. The cassette is a role slider (arrows 5 seconds, pages 30), Space plays and pauses. A click from a script on play (the library's preview) does the round: it plays, goes to the next song, lets the tape go slack and tightens it, goes back with the left reel, pauses and rewinds.
| Name | File | Default | What it changes |
|---|---|---|---|
SIDE | script.js | [['Cinder Blue', '3:41'], ['Coastline FM', '4:05'], …] | The songs on side A, written on the card: the title and how long it lasts (minutes:seconds). The tape and the packs are made to this length. |
TITLE | script.js | Long drive, June | The title written by hand on the cassette's label. |
BRAND | script.js | AUBADE | The tape's make, printed on the label's stripe. |
WIND | script.js | 1.5 | The seconds the fast wind takes to get to the next song. |
SLACK | script.js | 1 | How loose the tape can get when a reel is turned by hand: 0 never, 2 a lot. |
WEIGHT | script.js | 420 | How heavily a loose loop of tape hangs, in pixels a second squared. |
TENSION | script.js | 0.32 | The seconds the reels take to pull a loose loop tight again. |
COAST | script.js | 0.14 | How long a reel spins on by itself when the tape stops pulling it, in seconds. |
LIGHT | script.js | 1 | How far the pointer moves the reflections on the plastic (0 = not at all). |
For the site of a musician or an independent label, the page of a cassette reissue, a playlist made like an old mixtape, a radio station or a podcast with an analogue feel: a player that looks like a real object to hold.
The player is at most 380 pixels wide and the cassette narrows with it (at 390 it is 330 wide): the canvas is painted again at the right size. A finger takes a reel (the left or right half of the cassette) and turns it without scrolling the page: touch-action none only on the cassette. The keys are a quarter of the row wide and 46 pixels tall, the card's lines 30. The reflections follow the finger while it touches the player.
The reels do not turn and the tape never goes slack: the packs only change size with the time, which runs all the same while it plays. The fast wind gets there in a moment with a fade, the keys and the head change position at once, the player fades in without the cassette dropping in. Turning a reel by hand still moves the time, forward or back.
GSAP 3.15.0, Instrument Sans (font variabile) 5.3.0, Caveat (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 20 KB; the libraries: GSAP 28 KB.
Tested on 17 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.