
The needle gauge
Counters, Physics, SVG
NewFree
A music player with a turntable seen from above, and no sound: the record is the player. The vinyl has fine grooves, a band for each song and a light that stays still while the record turns underneath it; play starts the platter with its inertia, the arm lifts, goes to the lead-in and comes down, and as the side plays it follows the groove towards the middle. The time is the groove itself: holding the record and pulling it back goes back through the song, like a scratch.
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 turntable player</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">
<!-- the turntable player: a music player card whose top half is a turntable seen from above. The record turns
under a light that stays still, the arm cues onto the groove and follows it inwards, and the record can be
held and scratched. The grooves, the label and the light are painted on canvases, the arm is SVG. No sound -->
<section class="player" aria-label="Turntable player" data-player>
<div class="deck" data-deck>
<div class="platter" aria-hidden="true" data-platter></div>
<canvas class="disc" aria-hidden="true" data-grooves></canvas>
<canvas class="disc disc--spin" aria-hidden="true" data-spin></canvas>
<div class="spindle" aria-hidden="true" data-spindle></div>
<svg class="arm" aria-hidden="true" data-arm-svg></svg>
</div>
<div class="info">
<p class="info__title"><span class="roll" data-title></span></p>
<p class="info__by"><span data-artist>Ines Arlow</span> · <span data-album>Slow Weather</span></p>
</div>
<div class="side" role="slider" tabindex="0" aria-label="Position on side A" aria-valuemin="0" data-side>
<div class="side__bands" data-bands></div>
<span class="side__hover" aria-hidden="true" data-hover></span>
</div>
<div class="times" aria-hidden="true"><span data-at>0:00</span><span data-left>0:00</span></div>
<div class="buttons">
<button class="btn" type="button" aria-label="Previous song" data-prev><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 6v12M18 6.5v11L9.5 12z"/></svg></button>
<button class="btn btn--play" type="button" aria-label="Play" aria-pressed="false" data-play data-autoclic><svg viewBox="0 0 24 24" aria-hidden="true"><polygon data-a/><polygon data-b/></svg></button>
<button class="btn" type="button" aria-label="Next song" data-next><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M17 6v12M6 6.5v11l8.5-5.5z"/></svg></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.
32 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 turntable player” from Animorio and fit it into my siteThe side is a row of stretches, each with a start and an end time and a start and an end radius: the lead-in, the songs, the gaps between one song and the next, the lead-out down to the locked groove. The groove's pitch is steady, so inside a song the radius falls in step with the time and each band is as wide as the song is long; a gap between songs lasts 2.5 seconds but takes a wide step. radiusAt(t) and timeAtRadius(r) go from one to the other: the arm goes where the time says, and an arm moved by hand tells the time.
A record is the same in every direction, so it is painted once from its radius: at four samples a pixel the look of that radius is worked out (how light it is, how much the grooves reflect, how much the smooth vinyl reflects, how much of it is record) and each value is averaged over one pixel, so the fine grooves never make moiré. In the songs the groove is a sine 1.3 units apart with a slightly uneven phase, plus the loud and quiet passages that make it wider or narrower; the gaps are smooth and dark, the edge and the ring round the label are raised. Each pixel takes its value from its distance to the middle: a dark disc and two masks, grooves and smooth vinyl.
A round groove reflects the light only along the radius that points towards it: on a record you see a fan towards the light and a weaker one on the opposite side. Each fan is a conic gradient (createConicGradient) with 240 stops of Gaussians, cut by the mask with destination-in and added to the disc with screen: wide and grainy on the grooves, narrow and glossy on the smooth vinyl. The painted disc does not turn, so the light stays still while the label turns; the pointer moves the light's angle by a few degrees, and the platter's aluminium ring uses the same light through a CSS conic-gradient.
The stylus sits on a circle round the pivot, of radius L a little longer than the distance d from the pivot to the middle (the overhang). For a groove of radius r the angle comes from the triangle: cos θ = (d² + r² − L²) / 2dr, and from there the stylus's point and the arm's angle. The headshell is turned by an angle worked out so the cartridge lies along the groove two thirds of the way in, so the tube runs from the pivot to an off-axis collar. Lifting, the arm grows a little and its two shadows (a sharp one and a soft one) move away from the light; the cue lever turns. A GSAP timeline lifts, carries and lowers in CUE seconds.
The platter has a speed that the motor brings to RPM × 6 degrees a second with a limited torque and a soft arrival, and that the brake takes away in BRAKE seconds. A hand on the record gives an angle the record chases with a lag of SLIP seconds, and its speed stays with the record when it is let go. With the stylus down the time moves on by how far the record turns divided by the right speed: when starting the song runs slow, and pulling back goes back. Everything runs on GSAP's ticker with the step held to 1/30 of a second, and the ticker comes off when nothing moves.
The bands under the title are a role slider: as wide as the songs, the played part in the label's colour, the band playing taller and with the playhead. A click or a drag carries the lifted arm to that point and lowers it if the music plays; arrows and pages move by 5 and 30 seconds, Space plays and pauses. The play button has aria-pressed. A click from a script on play (the library's preview) does in turn play, a pretend scratch, the next song and pause.
| Name | File | Default | What it changes |
|---|---|---|---|
SIDE | script.js | [['Paper Moons', '3:48'], ['Glasshouse', '4:12'], …] | The songs on the side, from the edge inwards: the title and how long it lasts (minutes:seconds). Each band on the record is as wide as its song is long. |
RPM | script.js | 33.33 | The record's speed, in turns a minute (45 for a single); the label prints it. |
SPINUP | script.js | 1.2 | The seconds the motor takes to bring the platter up to speed. |
BRAKE | script.js | 1.8 | The seconds the platter takes to stop when the music is paused. |
CUE | script.js | 0.45 | The seconds the arm takes to come down onto the record. |
SLIP | script.js | 0.05 | How much the record slips under the hand holding it, in seconds of lag. |
SHEEN | script.js | 1 | How much the vinyl shines: 0 matte, 1 as pressed, 2 glossy. |
LIGHT | script.js | 1 | How far the pointer moves the light (0 = not at all). |
For the site of a musician, a label or a record shop, the page of an album or a vinyl reissue, a curated playlist, a podcast with an old-time feel: a player that should feel like a real object.
The card is at most 380 pixels wide and the turntable narrows with it (at 390 it is 358 wide). A finger takes the record and turns it without scrolling the page (touch-action none only on the record, the headshell and the bands); the headshell answers in a 42-pixel circle and the buttons are 48 and 64. The light follows the finger while it touches the card.
The platter does not turn and the arm does not move: it fades out and comes back already in place, up or down; the time runs all the same while it plays. The title changes with a fade, the play icon and the bands change at once, and the card fades in. The record can still be turned by hand, which holds it without slipping.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 20 KB; the libraries: GSAP 28 KB.
Tested on 7 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.