Skip to content
animorio
IT
All components

The sliding records

Carousels, Physics, TransitionsGSAP, CSS onlyAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

The new records of a record shop, each vinyl in its sleeve: when the row sets off the records are left behind, slide out of the open side and roll, their labels turning; when it stops they run back in and knock the back, and the sleeve gives a little jolt. The record in front stands half out, its name under it.

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 sliding records</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/syne@5.3.0/index.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/host-grotesk@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the records: a row of sleeves, each with its record inside. The row moves on a spring and the records feel
         it: they slide out of the open side of their sleeves and roll when it sets off, knock the back of the sleeve
         when it stops, and the record in front of you stands half out. The sleeves and the records are drawn by the
         script -->
    <section class="crate" role="region" aria-roledescription="carousel" aria-labelledby="crate-title" data-crate>
      <header class="crate__head">
        <h2 class="crate__title" id="crate-title">New in this week</h2>
        <div class="crate__arrows">
          <button class="arrow" type="button" aria-label="Previous record" data-prev>
            <svg viewBox="0 0 24 24" aria-hidden="true">
              <path class="arrow__step" d="M14 6.5 8.5 12l5.5 5.5"/>
              <path class="arrow__wrap" d="M8 9.5h6.5a4 4 0 0 1 0 8H10M10.5 6.5 7.5 9.5l3 3" transform="matrix(-1 0 0 1 24 0)"/>
            </svg>
          </button>
          <button class="arrow" type="button" aria-label="Next record" data-next data-autoclic>
            <svg viewBox="0 0 24 24" aria-hidden="true">
              <path class="arrow__step" d="M10 6.5 15.5 12 10 17.5"/>
              <path class="arrow__wrap" d="M8 9.5h6.5a4 4 0 0 1 0 8H10M10.5 6.5 7.5 9.5l3 3"/>
            </svg>
          </button>
        </div>
      </header>

      <div class="crate__view" tabindex="0" aria-describedby="crate-help" data-view>
        <div class="crate__row" data-row></div>
      </div>
      <p class="sr" id="crate-help">Arrow keys to move along the records, Home and End for the first and the last.</p>

      <div class="crate__foot">
        <div class="caption" data-caption>
          <p class="caption__title" data-cap-title></p>
          <p class="caption__artist" data-cap-artist></p>
        </div>
        <div class="caption caption--end" data-caption-end>
          <p class="caption__price" data-cap-price></p>
          <p class="caption__meta" data-cap-meta></p>
        </div>
      </div>
      <div class="rail" aria-hidden="true"><span class="rail__thumb" data-thumb></span></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.

53 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 sliding records” from Animorio and fit it into my site

What it does

On a white background, alone in the middle, the carousel of a record shop’s new records: at the top New in this week and two round buttons with arrows; under it, a row of square sleeves of 300 pixels with drawn covers (a planet with its orange ring on cobalt blue, PAPER WEATHER in huge letters on ochre with three waves, coral circles and two lanes on petrol green, a purple fig with a lightning bolt, HUM in cream on red, night blue arcs with an orange sun, a cream staircase on black). The first, Slow Orbit by Ines Varga, has its black record half out on the right, with its grooves, the lamp’s reflection and the orange label with the title written round it; the other sleeves show a sliver of record on their open side. Under the row the title, the artist, the price (€28) and LP, 2026, and a thin rail with a dark bar that says where the record is in the row. Pressing the next arrow the row sets off to the left: the records are left behind and slide out of their sleeves rolling, the labels turning; then the row brakes, the records run back in and knock the back, the sleeve gives a little jolt, and the new record rolls out halfway, goes a little further and comes back. A record that comes out too far knocks the next sleeve, which tilts for a moment. The name underneath slides the way the row goes and the bar runs and stretches while the row moves. On the last record the next arrow turns into a curl and takes the row back to the start, through all of it; on the first the back arrow goes to the last. The row is dragged with the mouse or a finger, thrown and caught on the nearest sleeve, and past the ends it pulls like a rubber band. With the mouse over a sleeve its record comes out a little; a click on a sleeve at the side goes there. With the row focused the arrow keys, Home and End work.

How it works

One number for the row

The row is a single number, p: the record in front as a fraction. A spring takes it to the chosen record, a = ω²·(goal − p) − 2ζω·v, with ω and ζ from GLIDE; the stiffness is divided by the square root of the distance, so a jump of six records starts with the same push as a single step and lasts only a little longer. Each sleeve stands at (i − p) times the step, in pixels.

The push the records feel

That same a, times the step, is the row’s acceleration in pixels, already smooth because the spring works it out rather than the difference between two frames. Each record is a mass on a spring (SPRING) towards its place, OUT for the one in front and REST for the others, and the row accelerating pushes it the other way: by PUSH and by two thirds, what a disc rolling on its edge feels (its moment of inertia is half m r²).

The back and the next sleeve

Below zero is the back of the sleeve: the record bounces off it keeping KNOCK of its speed, and the sleeve takes 22 per cent of the knock, jolting on its own spring, a little sideways and a little turned. The other way, a record goes at most as far as the next sleeve, which it knocks the same way; the last one in the row as far as two thirds of its sleeve.

It rolls, and the light stays

A rolling record turns by the way it has gone over its radius: the label, an SVG with the title and the side written round it with textPath, turns by u / R. The grooves are a repeating-radial-gradient with the gaps between the songs; the reflection is a conic-gradient on a layer that does not turn, as the lamp stays where it is.

Hands, keys and ends

With the mouse or a finger the row chases the pointer on a stiff spring, so the push is smooth there too, and past the ends on a rubber band. Let go, it takes the speed of the last 90 milliseconds, looks at where it would get in THROW seconds and is caught on the nearest record; touch-action pan-y leaves vertical page scrolling to the finger. Arrow keys, Home, End and the sideways wheel; at the ends the buttons turn into a curl that goes to the other end.

The step and the phone

The step between sleeves is worked out by the script: the next sleeve starts --gap pixels after the record that stands out, so at rest the record covers nothing. The group of sleeve and record stands in the middle; when it does not fit, the sleeve lines up with the title and the record runs to the edge of the screen, where the row goes past the margins. When everything is still the loop switches off.

Values to change

NameFileDefaultWhat it changes
OUTscript.js0.46How far the record in front stands out of its sleeve, as a part of the sleeve’s side.
RESTscript.js0.05How far the other records peek out of their sleeves when nothing moves.
PUSHscript.js1.3How much the records feel the row moving: 0 not at all.
SPRINGscript.js[110, 0.3]The spring that takes a record back to its place: how stiff it is and how much it is held back.
KNOCKscript.js0.35How much of its speed a record keeps when it knocks the back of its sleeve.
GLIDEscript.js[6.5, 0.8]The row’s spring: how quickly it gets to a record and how much it is held back.
THROWscript.js0.22The seconds a thrown row runs on before it is caught (how far a flick goes).
RECORDSscript.js[['Slow Orbit', 'Ines Varga', 'LP, 2026', '€28', 'orbit'], …]The records: title, artist, format and year, price, cover (orbit, weather, swim, fig, hum, radio, stairs).
--sizestyle.css300pxThe side of a sleeve (260 on a phone).
--gapstyle.css24pxThe space between the record out of its sleeve and the next sleeve.

Try them in the Playground

When to use it

For the new arrivals of a record shop or a bookshop, an online shop’s featured products, a label’s albums, the podcasts or playlists of an app: wherever each thing has a square cover and is worth pulling a little out of the row.

On phones

The sleeves go down to 260 pixels and the row reaches the edges of the screen: the sleeve in front lines up with the title, 16 pixels from the edge, and its record comes out by 120 pixels to the right edge, faded a little. A finger drags and throws the row (up and down scrolls the page), the buttons are 42 pixels and a tap on a sleeve at the side goes there.

With reduced motion

The row does not run: it fades out and back in already on the new record, with the records already in place (they change halfway through the fade); no sliding, bouncing or jolting sleeves, and the mouse over a sleeve pulls nothing out. The name changes with a fade and the component comes in with a fade only. Dragged, the row follows the hand, and let go it fades onto the nearest record.

Pitfalls

  • With the row’s spring too quick the push lasted an instant and the records, shut in their sleeves, had no time to move: the effect did not show. The row is softer now, the records sit on a gentler spring and at rest each peeks out by a sliver (REST), so it can be seen setting off.
  • The record in front covered the title of the next cover. The step between sleeves is now worked out by the script from how far the record stands out, and a record that slides too far stops against the next sleeve, which takes the knock, instead of passing over it.
  • An acceleration taken from the difference between two frames is noisy (frames do not come at equal intervals) and would make the records shake: the one the spring works out at every step is used, and with a finger too the row chases the pointer on a stiff spring instead of jumping to where the finger is.
  • A record has to lie under its sleeve but over the next one: the record is inside its sleeve’s element and the elements have a z-index that falls from left to right.
  • On a phone the group of sleeve and record, centred, left the record almost all inside, with a sliver of label: when the group does not fit the sleeve lines up with the title and the record runs to the edge of the screen.
  • With reduced motion the records moved an instant before the row faded out: now they change halfway through the fade.

Libraries

GSAP 3.15.0, Syne (font variabile) 5.3.0, Host Grotesk (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 12 KB; the libraries: GSAP 28 KB.

Testing

Tested on 6 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