Skip to content
animorio
IT
All components

The turntable player

Players, Physics, SVGGSAP, CanvasAdvancedCompressed weight: Demo 20 KBGSAP 28 KBFreeDownload the zip

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.

The code

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>

Playground

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

Or ask Claude Code for it

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 site

What it does

On a white background an ivory lacquered card 380 pixels wide. At the top the turntable seen from above: the aluminium platter, a black record with the orange label of Slow Weather by Ines Arlow (a sun setting into the sea, the title and the artist written round, Side A and 33⅓), and on the right the arm on its rest, with the pivot, the counterweight, the headshell and the cue lever. Below, the song's title, the artist and the album, the side unrolled into five bands (one per song, as wide as it is long), the time gone and the time left, and the back, play and forward buttons. On the record you see the fine grooves, the five bands with their loud and quiet passages, the smooth shiny gaps between one song and the next, the lead-in, the lead-out and the raised edge; the light makes two opposite fans on the grooves that stay still while the record turns, and moves a little with the pointer. Pressing play the motor brings the platter up to 33 turns in a little over a second and the label starts to turn; the arm lifts, the lever flicks, the arm goes to the lead-in and comes down, and its shadow shrinks until it touches it: within a second and a half the music has started. From there the time runs with the record's rotation and the arm follows the groove towards the middle. Pause lifts the arm where it is and the brake stops the platter in a little under two seconds; forward and back lift the arm, carry it to the song's band and lower it again, and the title slides. The record is taken with the mouse or a finger and follows the hand, slipping a little on the platter: pulling it back the time goes back, letting it go the motor brings it back up to speed. The headshell is taken by its finger lift and carried anywhere, and the time follows the arm; off the record it goes back to its rest. On the bands under the title a click or a drag carries the arm to that point. At the end of the side the arm goes back to its rest by itself and the platter stops.

How it works

The side in times and radii

The 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.

Grooves painted from the radius

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 light that stays still

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 arm and its geometry

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, the motor and the hand

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, the keys and the preview

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.

Values to change

NameFileDefaultWhat it changes
SIDEscript.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.
RPMscript.js33.33The record's speed, in turns a minute (45 for a single); the label prints it.
SPINUPscript.js1.2The seconds the motor takes to bring the platter up to speed.
BRAKEscript.js1.8The seconds the platter takes to stop when the music is paused.
CUEscript.js0.45The seconds the arm takes to come down onto the record.
SLIPscript.js0.05How much the record slips under the hand holding it, in seconds of lag.
SHEENscript.js1How much the vinyl shines: 0 matte, 1 as pressed, 2 glossy.
LIGHTscript.js1How far the pointer moves the light (0 = not at all).

Try them in the Playground

When to use it

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.

On phones

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.

With reduced motion

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.

Pitfalls

  • Mid-frequency noise in the grooves made the record look full of rings, like a 78, and the five songs could no longer be read: the grooves are now a fine sine with a slightly uneven phase, the loud and quiet passages change little, and the gaps between songs are darker and glossier.
  • With the same light on the grooves and on the smooth vinyl only the glossy dashes of the gaps between songs showed: the fan on the grooves is wider and stronger, the one on the smooth vinyl narrower.
  • The ⅓ sign is not in Archivo's latin subset and the canvas took it from another font: the label's 33⅓ is drawn by hand, a small 1 and 3 and a slash.
  • On the automatic return the arm passes over the whole record, and the time following it rewound every song in a second: during the return the time stays at the end of the side, and goes back to 0:00 when the arm is on its rest.
  • With the arm towards the middle the counterweight left the card at the top right: the pivot was put where the counterweight stays inside even at the end of the side, at one and a half times the record's radius from the middle, as on a real nine-inch arm.
  • With the headshell turned in towards the record the tube cannot run straight from the pivot to the stylus: it runs from the pivot to the headshell's collar, a little off axis, and the angle is worked out from the same geometry.
  • Between the arm coming down and two seconds of lead-in the time stayed at 0:00 for four seconds after play, and in the thumbnail the player looked dead: the arm starts swinging while it lifts and starts coming down before it gets there, the drop takes CUE = 0.45 seconds and the lead-in a third of a second, so the music starts within a second and a half.
  • Going to the next song within the same second (both at 0:00) the time left stayed the old song's: the readouts are written again when the song changes too, not only the second.

Libraries

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.

Testing

Tested on 7 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

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.

All components