Skip to content
animorio
IT
All components

The steeping tea

Cards, Physics, Hover, TransitionsCanvas, GSAP, CSS onlyAdvancedCompressed weight: Demo 22 KBGSAP 28 KBFreeDownload the zip

The card of a green tea in the shop of a small tea house: over a glass cup of hot water hangs a silk pyramid of tea. With Steep, or a click or a tap on the cup, the bag goes down into the water swinging on its string and the tea comes out of it in plumes that sink and curl, because it is heavier than water, while the water slowly turns gold-green and steam rises from the rim. The fluid is real, solved on a grid (Stam's stable fluids). Lifted, the bag drips; the colour stays.

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 steeping tea</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the card of a green tea in the shop of a small tea house. Over a glass cup of hot water hangs a silk pyramid
         of tea on its string: with Steep, or a click or a tap on the cup, it goes down into the water swinging on the string, and the
         tea leaves it in plumes that curl and sink, while the water slowly turns gold-green. Lifted, it drips; the
         colour stays. The cup, the water and the tea are drawn by the script -->
    <article class="tea" aria-labelledby="tea-name" data-tea data-autoclic>
      <div class="tea__shot" data-shot>
        <canvas class="tea__canvas" role="img" aria-label="A glass cup of hot water, with a silk pyramid of tea hanging over it on its string" data-canvas></canvas>
      </div>
      <div class="tea__body">
        <p class="tea__kind">Green tea · Shizuoka</p>
        <div class="tea__head">
          <h2 class="tea__name" id="tea-name">Sencha Asamushi</h2>
          <p class="tea__price">€14</p>
        </div>
        <div class="tea__meta">
          <p class="tea__notes">Grassy · Marine · Sweet</p>
          <p class="tea__weight">50 g</p>
        </div>
        <div class="brew">
          <p class="brew__guide"><span class="brew__label">Brew</span> 70 °C · 90 s</p>
          <button class="pill pill--steep" type="button" data-steep>
            <span class="pill__icon" aria-hidden="true">
              <svg class="steep-icon" viewBox="0 0 24 24">
                <path class="steep-icon__water" d="M3.5 17.2c1.6-1 3.1-1 4.7 0s3.1 1 4.7 0 3.1-1 4.7 0 2.4.8 2.9.4"/>
                <g class="steep-icon__bag" data-bag-icon>
                  <path d="M12 2.4v5"/>
                  <path d="M8.2 7.6h7.6L12 14.4z"/>
                </g>
              </svg>
            </span>
            <span class="pill__label" data-label>Steep</span>
          </button>
        </div>
        <div class="buy">
          <button class="pill pill--add" type="button" data-add>
            <span class="pill__clip" aria-hidden="true">
              <span class="add__fill" data-fill>
                <svg class="add__wave" viewBox="0 0 96 8" preserveAspectRatio="none" data-wave><path d="M0 4c6-4 12-4 18 0s12 4 18 0 12-4 18 0 12 4 18 0 12-4 18 0 6 2 6 2V8H0z"/></svg>
              </span>
            </span>
            <span class="pill__icon" aria-hidden="true">
              <svg class="add__glyph" viewBox="0 0 24 24" data-cart><path d="M3.4 4.6h2.3l2.1 9.6a1.7 1.7 0 0 0 1.7 1.3h7.1a1.7 1.7 0 0 0 1.7-1.3l1.4-6.4H6.6"/><path d="M10.2 19.4a.9.9 0 1 0 0 .01M16.6 19.4a.9.9 0 1 0 0 .01"/></svg>
              <svg class="add__glyph add__tick" viewBox="0 0 24 24" data-tick><path d="M5.6 12.6l4.1 4.1 8.7-9.2"/></svg>
            </span>
            <span class="pill__label" data-label>Add to cart</span>
          </button>
          <button class="pill pill--undo" type="button" data-undo hidden><span class="pill__label">Undo</span></button>
        </div>
        <p class="sr" aria-live="polite" data-live></p>
      </div>
    </article>
  </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.

38 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 steeping tea” from Animorio and fit it into my site

What it does

On a white background a white card with round corners. At the top the picture, drawn by the script: a sage green wall lit by a window on the left, a pale table and on it a clear glass cup with a handle, full of hot water, with its long shadow to the right. Through the water the wall and the edge of the table look bent, as behind a lens; the glass has dark edges, the window reflected in strips, a shining rim and a thick base. Over the cup hangs a silk pyramid full of green leaves, on its string and a paper tag with a leaf and the word SENCHA, held by a hand outside the picture: as the card arrives it rises and the bag sways a little. Below: Green tea · Shizuoka, the name Sencha Asamushi with €14, the notes Grassy · Marine · Sweet with 50 g, a thin line, the guide Brew 70 °C · 90 s with the Steep button, and the dark green Add to cart button. With the pointer over the picture the hand gets ready: the bag comes down a few pixels into the mouth of the cup and sways, without touching the water, and the cursor becomes a hand; on the rest of the card the pointer does nothing. Pressing Steep, or with a click or a tap on the picture, the hand lowers the bag: it goes down drifting a little to the side, so it swings on its string, touches the water with a ring that spreads over the surface, is dunked twice and stays, its string going slack when the water holds it up. The tea comes out of the bag: a dark yellow and green cloud that breaks away and sinks in threads, their mushroom heads curling over; at the bottom the tea gathers, spreads and mixes in, and the whole cup slowly turns gold-green, stronger at the bottom. On the table, in the cup's shadow, the light gathered by the water takes the colour of the tea. Two or three threads of steam rise from the rim and thin out as time goes by. The Steep button becomes Lift and its little bag goes down under its water line. With Lift, or another click on the picture, the bag comes back up wet and drips: a drop swells at its tip and falls, often at first and then more and more rarely, and every drop makes a ring and a little plume. The colour stays in the cup. Add to cart swells and widens its word with the pointer over it; pressed, it becomes Adding and fills like a cup, its surface rocking, then Added with a tick that draws itself, in the pale yellow of the tea, and Undo appears beside it; after a few seconds it is Add to cart again.

How it works

The scene, drawn once

Everything is drawn on a board of 324 by 300 points, scaled to the canvas. The wall, the table and the shadows are one layer; the cup is three more, made again only when the size changes: the back (the glass seen through, the far rim), the water's surface (the window mirrored, the dark and bright meniscus) and the front (the dark edges, the window's strips of light, the rim, the thick base, the handle). The water is a lens: every column of pixels behind it is taken from another column of the background, wider in the middle and narrow at the sides, and shifted up and down, so the edge of the table bends.

The water is a real fluid

The velocity lives on a grid of cells CELL pixels wide, cut to the shape of the water: the glass is a wall the pressure does not cross. Every step the tea pushes the water down as strongly as it is concentrated (SINK), the eddies are kept alive by a push round every swirl (vorticity confinement, CURL) only where there is tea, the bag drags the water along and a little viscosity draws every cell towards the mean of its neighbours; then the velocity carries itself back along the flow (semi-Lagrangian) and a pressure, found with over-relaxed Gauss-Seidel starting from the last one, takes the divergence out so the water never squeezes (Stam's stable fluids).

The tea, on a finer grid

The tea lives on a grid twice as fine and goes with the flow forwards and back again (MacCormack, held between its four neighbours), so its threads stay fine. The bag gives it from its lower faces, with a noise that changes seven times a second and lets it out in several threads: a lot as it goes in and whenever it moves, then less and less. Slowly the tea dissolves into an even colour of the whole cup, a little stronger at the bottom. It is drawn as light taken away (Beer-Lambert): the colour of the tea at full strength in :root says how much it takes of each channel, a table of 1024 entries turns any depth of tea into the colour that passes, and the picture multiplies the water.

The bag on a pendulum

The top of the bag is a Verlet point hanging from a string that pulls but never pushes: when the water holds the bag up the string goes slack and bends. Its weight and drag move from those of the air to those of the water by how deep it is, in steps of 1/240 of a second; the bag's angle follows the string on a damped spring. The hand is a GSAP timeline, started only by Steep or a click on the cup: it lowers the bag drifting a little to the side, dunks it twice and lets it be. In the water the bag looks wider, as seen through the curved glass, and drags the fluid.

Drops, rings and steam

Out of the water, a drop swells at the tip of the bag and falls, more often the fuller the bag is. Every drop that lands makes a ring on the surface (a bright and a dark ellipse that spread) and a little cloud of tea pushed down, which becomes a plume. The steam is three threads of points rising and swaying on two sines, drawn in strokes that widen and fade; it thins out over a minute, as the water cools.

The states, and rest

The tea steeps only when asked: Steep (Enter or Space too), a click or a tap on the picture; Lift or another click takes it out. The pointer over the picture only gives a foretaste: a GSAP tween lowers the hand 9 pixels with a bounce and gives the bag a small push, so it comes down into the mouth of the cup and sways, always above the water; leaving the picture it goes back up, and on the rest of the card nothing happens. Add to cart fills like a cup (a film with a wave on top that rises), then Added with its tick, Undo, and back to rest; an aria-live status says every step. The time step is limited to 1/30 of a second; the pendulum sleeps under half a pixel a second, the fluid once the bag gives nothing more and the tea has mixed in, the steam after a minute: then nothing is drawn any more.

Values to change

NameFileDefaultWhat it changes
STEEPscript.js1.3The seconds the hand takes to lower the tea bag into the water.
LIFTscript.js1.1The seconds it takes to lift it out again.
SWINGscript.js0.4How freely the bag swings on its string in the air: the share of its speed it keeps each second (0.05 to 0.9).
SINKscript.js64How hard the tea falls through the water: how much heavier than the water it is, in pixels a second squared.
CURLscript.js4How much the falling tea curls over into eddies (0 = smooth streams).
STRENGTHscript.js1How much tea the leaves give: more is a darker cup.
STEAMscript.js0.8How much steam rises from the cup, from 0 to 1.
CELLscript.js2.6The size of a cell of the water's simulation, in pixels: smaller is finer and slower.
WORDSscript.js['Lift', 'Adding', 'Added']The steep button with the bag in the water, then Add to cart while it works and once done.

Try them in the Playground

When to use it

For the card of a tea, an infusion or a coffee in an online shop; wherever a product is best understood seen at work (a dye, an ink, a bath salt, a syrup), and for a product page that wants a moment to watch before the cart.

On phones

The card is 340 pixels wide, or the screen minus 32, and the scene is drawn again at the real size. A tap on the picture puts the bag in to steep and another takes it out, like Steep and Lift; the mouse's foretaste is not there on a phone, since the tap does it all; the buttons are 50 and 42 pixels high. Under 360 pixels the name and the pills tighten a little. The simulation is the same as on a computer: the grid is small (about 45 by 55 cells, and twice that for the tea) and costs a couple of milliseconds a frame.

With reduced motion

With the pointer over the picture the bag stays still. Steeped, it does not go down or swing: it appears in the water in a fade and leaves the same way. No plumes, drops, rings or steam: the water takes its colour in a slow fade and keeps it. The buttons' words change in a fade and the pills do not swell.

Pitfalls

  • In the first version the pointer passing over the card already lowered the bag: whoever went to press Steep found it had already become Lift, pressed it and pulled the bag out, and the tea never steeped. Now steeping starts only on an explicit action (Steep, Enter or Space, a click or a tap on the cup) and passing over the picture is only a foretaste: the bag comes down 9 pixels into the mouth of the cup and sways, without touching the water.
  • The little push that makes the bag sway as it arrives was a shift of 4 pixels: in Verlet a shift is a speed, and the bag went right round over its tag. Now the push is a small speed, given by moving the previous position.
  • Vorticity confinement kept the water moving for ever, even after the tea had mixed in: the eddies fed themselves and the scene never stopped drawing. Now it pushes only where there is tea, and once the bag gives nothing more the damping and the mixing grow, until the fluid stops and sleeps.
  • Now and then the tea made straight horizontal streaks with stepped ends: on Stam's grid, with velocity and pressure at the same point, a noise one cell wide is left, and vorticity confinement blows it up along the rows of the grid. A little viscosity (every cell moves towards the mean of its neighbours, 12 per cent a step) takes it out; too much (30 per cent) took the curls out too and the tea sank smooth and symmetric like an anchor, so CURL went up from 3 to 4.
  • The tea came out in a single thread from the tip: the bag held the water still inside it and the tea could only leave from its bottom corner. With a lighter drag and a noise in the way it comes out, it leaves in several threads, from the faces.
  • Seen through the interpolation, the picture of the tea faded against the glass, because the cells outside the water were zero: a ring of cells outside takes the mean of their neighbours inside.
  • GSAP read the CSS translateY(130%) of Add to cart's fill as pixels and added the yPercent to it: the fill stopped below the pill. At the start y is set to zero.
  • Undo had overflow hidden to grow from nothing, and it cut its own focus ring: now the overflow is hidden only while it grows.
  • The pendulum never fell asleep: the threshold was 0.02 pixels a second, and the swing dies slowly. Now it is half a pixel a second, less than a hundredth of a pixel a frame.
  • With the tag over the middle of the cup, the paper covered the water's surface with the bag in: the hand goes down only as far as keeps the tag above the far rim, and the cup stands lower to make room for the bag.

Libraries

GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Mona Sans (font variabile) 5.3.0.

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