Skip to content
animorio
IT
All components

The sloshing inks

Carousels, Physics, SVGGSAP, CSS onlyAdvancedCompressed weight: Demo 13 KBGSAP 28 KBFreeDownload the zip

The inks of a small ink maker, each in its glass bottle, on a shelf that slides: when the shelf sets off the ink is left behind and climbs the glass, when it brakes it runs to the other side, and when it stops it rocks until it lies flat again. Where the wave went the glass stays stained, and the stain runs back down in streaks. Each bottle rocks at its own pace: the wide ones slowly, the narrow ones quickly.

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 sloshing inks</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/alegreya@5.3.0/index.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the inks of a small ink maker, each in its glass bottle on a shelf that moves. The ink feels the shelf: it
         is left behind when the shelf sets off, runs up the glass when it brakes, rocks when it stops, and the glass
         stays stained where the wave went until the film drains back down. The bottles are drawn by the script -->
    <section class="shelf" role="region" aria-roledescription="carousel" aria-labelledby="shelf-title" data-shelf>
      <header class="shelf__head">
        <h2 class="shelf__title" id="shelf-title">Fountain pen inks</h2>
        <div class="shelf__arrows">
          <button class="arrow" type="button" aria-label="Previous inks" aria-controls="shelf-track" data-prev>
            <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14 6.5 8.5 12l5.5 5.5"/></svg>
          </button>
          <button class="arrow" type="button" aria-label="Next inks" aria-controls="shelf-track" 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__back" d="M18.5 12H9M13 7.5 8.5 12l4.5 4.5M5.5 6.5v11"/>
            </svg>
          </button>
        </div>
      </header>

      <div class="shelf__view" tabindex="0" aria-describedby="shelf-help" data-view>
        <div class="shelf__track" id="shelf-track" data-track></div>
      </div>
      <p class="sr" id="shelf-help">Arrow keys to move along the inks, Home and End for the first and the last.</p>
      <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 sloshing inks” from Animorio and fit it into my site

What it does

On a white background, alone in the middle, the carousel of a fountain pen ink maker, 720 pixels wide: at the top Fountain pen inks and two round buttons with arrows; under it, a row of tall tiles with rounded corners, each the pale colour of its ink, with a drawn glass bottle on it and under it the name, the price, the colour and the size. The bottles are all different: a low wide one with a black ridged cap (Kingfisher, teal), a tall square one (Oxblood, dark red), a round flask with a cork (Moss, green), a cone-shaped inkwell with a brass cap (Saffron, golden yellow), a big 100 ml one (Night Heron, blue black) and a round one with a wooden cap (Damson, plum). Two and a half tiles show: the third is cut by the edge. Inside each bottle the ink has its surface seen a little from above, paler, the thick glass of the walls and the bottom takes its colour and two still reflections sit on the glass. Pressing the next arrow the shelf slides by one tile: as it sets off the ink is left behind and rises against the glass on the side the shelf comes from; as it brakes it runs forward and climbs the other wall; once the shelf stops it rocks back and forth and in a second and a half lies flat. The wide bottles rock slowly, the narrow ones quickly, as a liquid really does. Where the wave climbed, the glass stays veiled with ink and the stain runs down in streaks. At the end of the row the next arrow turns into an arrow with a bar and takes the shelf back to the start in a long run, and all the inks slosh together; at the start the back arrow is off. The shelf is dragged with the mouse or a finger, thrown and caught on the nearest tile, and past the ends it pulls like a rubber band; a tap on a cut tile brings it in, a tap on a bottle in full view nudges it a little and its ink sways. With the shelf focused the arrow keys, Home and End work; against an end the shelf gives a little knock and comes back.

How it works

One number for the shelf

The shelf is a single number, x, the pixels it has gone. A spring takes it to the chosen stop, a = ω²·(goal − x) − 2ζω·v, with ω and ζ from GLIDE; the stiffness is divided by the square root of the distance in tiles, so the long run back to the start sets off with the same push as a single step. The stops are the tiles lined up with the title, the last one with the last tile against the right edge. That a, worked out by the spring at every step, is the bottles’ acceleration (the other way), already smooth: no differences between two frames.

The sloshing modes

The ink’s surface is the sum of the first three modes a push sideways can start in a tank W wide: z(u) = Σ ηₙ·cos(nπu) for n = 1, 3, 5, with u from 0 to 1 between the two walls. Each ηₙ is a spring with the true frequency of a liquid h deep, ωₙ² = g·k·tanh(k·h) with k = nπ/W, and the push moves it towards the height it would have under a steady push, (a/g)·4W/(n²π²): added up, they are the surface tilted by a/g. The g is chosen so that a bottle 100 wide and 60 deep rocks WAVE[0] times a second; ζ grows with the square root of n, so the ripples die before the big wave.

A liquid that stays believable

The push is held softly under two g with a hyperbolic tangent, because a flick of the hand would give four or five g and the ink would go straight up to the cap. A big wave sharpens as at the second order of a standing wave, KAPPA·η₁²/W·cos(2πu), higher at the crest and flat in the trough; and it does not climb past the bottle’s shoulder: the height is squeezed with tanh near the ceiling and the bottom.

The glass and the surface seen from above

Each bottle is an SVG with its outline and its inside. The ink is a path made again at every frame: everything under the far edge of the surface, cut by the inside with a clipPath. The surface seen a little from above is the band between the far edge and the near one, an ellipse in the round bottles and an even band in the square ones, with a thread of light on the near edge. The same path, cut by the outside outline and paler, colours the thick glass of the walls; the white reflections stay still on top, like a lamp.

The stain that runs

For each of the 33 points of the surface the script remembers the highest the far edge has reached: there the glass is wet. The wet line runs down at 26/FILM units a second, each point at a slightly different pace (two slow sines), so the stain runs in soft streaks; its edge is drawn smoothed over five points, in the ink’s colour at 20 per cent. When the surface rises again the line rises with it.

Hands, keys and the phone

With the mouse or a finger the shelf 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 stop. The sideways wheel does the same. A tap on a bottle in full view gives it a push on a spring of the shelf, and its acceleration adds to the shelf’s. On a phone the shelf runs to the edges of the screen with the first tile lined up with the title. When everything is still the loop switches off.

Values to change

NameFileDefaultWhat it changes
SLOSHscript.js0.3How much the ink feels the shelf moving: 0 not at all.
WAVEscript.js[1.5, 0.15]The ink in a bottle about 110 pixels wide: how many times a second it rocks and how much it is held back. Wider bottles rock more slowly, narrower ones more quickly.
FILMscript.js0.9The seconds the ink left on the glass takes to run back down.
GLIDEscript.js[7, 0.85]The shelf’s spring: how quickly it gets to a tile and how much it is held back.
THROWscript.js0.24The seconds a thrown shelf runs on before it is caught (how far a flick goes).
INKSscript.js[['Kingfisher', 'Teal blue', '50 ml', '€16', '#0f7f8a', 'pot'], …]The inks: name, colour, size, price, the ink’s colour and the bottle (pot, square, flask, cone, tall, classic).
--cardstyle.css280pxThe width of a tile (264 on a phone).
--gapstyle.css16pxThe space between two tiles (12 on a phone).

Try them in the Playground

When to use it

For the products of a shop that sells liquid things in glass: inks, perfumes, oils, syrups, sauces, cosmetics, a bar’s cocktail list. Wherever the product is looked at before buying it, and a little life in the row makes people want to scroll it.

On phones

The tiles go down to 264 pixels and the shelf reaches the edges of the screen: the first tile lines up with the title, 16 pixels from the edge, and about 90 pixels of the second one show. A finger drags and throws the shelf (up and down scrolls the page), the ink sloshes as with the mouse, the buttons are 44 pixels and a tap on a cut tile brings it in.

With reduced motion

The shelf does not run: it fades out and back in already at the new stop, and the ink stays flat, with no waves or stains; a tap on a bottle does not move it. Dragged, the shelf follows the hand, and let go it fades onto the nearest stop. The component comes in with a fade only.

Pitfalls

  • The page is a grid that puts the component in the middle, and its column took the minimum width of the whole row, 1700 pixels: on a phone the page grew wider and the browser zoomed everything out. The column is now minmax(0, 1fr) and the carousel has min-width 0.
  • In SVG the y axis goes down: the ink’s depth has to be taken as bottom minus surface. With the wrong sign tanh(k·h) is negative, the square root of the frequency gives NaN and the paths break.
  • A flick of the hand gave accelerations of four or five g and the ink went straight up to the cap, in a U shape: the push is held under two g with a hyperbolic tangent and the wave sharpens less (KAPPA 0.6).
  • With running paces that changed from one point to the next the stain on the glass turned into a row of pointed mountains: now the pace changes along two slow sines, the edge is drawn smoothed over five points and the stain is lighter.
  • The white reflections drawn on the glass wall, white on pale glass, did not show: they sit just inside, over the ink, where they read as light on the glass.
  • With reduced motion the shelf’s spring kept sliding it during the fade: in that mode the physics step stops at once and the shelf moves only halfway through the fade.
  • On a phone the shelf reaches the edges of the screen and the focus ring, drawn outside, ended up off the screen: there it is drawn inside, over the tiles.

Libraries

GSAP 3.15.0, Alegreya (font variabile) 5.3.0, Instrument Sans (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 13 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