Skip to content
animorio
IT
All components

The wine glass rating

Ratings, Physics, SVG, HoverGSAPAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

A wine is rated with five glasses: moving over them, the glass pointed at and the ones before it are poured one after the other, and the wine inside sloshes for real, on the glass’s own movements. With the top rating the glasses are filled to the brim and the wine spills over in drops. Once the rating is picked, the full glasses are raised for a toast: they lean towards each other, touch where their bowls really meet, with a gold spark, and the knock sets the wine swinging.

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 wine glass rating</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/cormorant@5.3.0/wght.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/cormorant@5.3.0/wght-italic.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- a tasting note given with wine glasses: the glass under the pointer and the ones before it are poured, and the
         wine in them sloshes for real, on the glasses' own movement; the top rating pours them to the brim and they
         spill. Picked, the full glasses are raised and brought together for a toast: they clink, bounce apart and the
         wine swings. Under the glasses there are five real radio buttons; the glasses are drawn by the script -->
    <form class="taste" data-taste onsubmit="return false">
      <p class="taste__ask" id="taste-ask">How was the 2019 Nebbiolo?</p>
      <fieldset class="taste__row" aria-labelledby="taste-ask" data-row data-autoclic>
        <svg class="taste__art" aria-hidden="true" data-art>
          <defs>
            <linearGradient id="taste-wine" x1="0" y1="-98" x2="0" y2="-38" gradientUnits="userSpaceOnUse">
              <stop class="taste__stop-wine" offset="0"/>
              <stop class="taste__stop-deep" offset="1"/>
            </linearGradient>
            <linearGradient id="taste-tint" x1="0" y1="0" x2="1" y2="0">
              <stop class="taste__stop-tint" offset="0"/>
              <stop class="taste__stop-clear" offset="0.45"/>
              <stop class="taste__stop-tint" offset="1"/>
            </linearGradient>
            <clipPath id="taste-inside"><path data-inside/></clipPath>
          </defs>
          <g data-shadows></g>
          <g data-glasses></g>
          <g data-drops></g>
          <g data-sparks></g>
        </svg>
        <div class="taste__choices" data-choices></div>
      </fieldset>
      <p class="taste__word" aria-hidden="true"><span data-word>Choose a glass</span></p>
      <p class="taste__thanks" role="status" aria-live="polite" data-thanks></p>
    </form>
  </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.

26 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 wine glass rating” from Animorio and fit it into my site

What it does

On a white background the question How was the 2019 Nebbiolo?, in a serif type; below it, five empty red wine glasses, clear glass with a cool grey edge, a long light on the left of the bowl and a shadow on the table; lower down, in grey italics, Choose a glass. Moving the pointer over them, the glass pointed at and the ones before it are poured from the left, one after the other: the red Nebbiolo rises in the bowl with a little bounce, its surface seen just from above as a lighter ellipse, and the pour sets it rocking. The glass under the pointer is lifted a touch and leans towards it: the wine stays level and sways, as in a real glass. Below reads the word for the rating, Not for me, Simple, Pleasant, Elegant or Unforgettable, in italics, blacker the higher the rating. With the top rating the glasses are filled to the brim: the wine that swings past the rim spills over in drops that fly out, fall and vanish before the table. Leaving, the glasses go back to the rating picked, or empty if there is none yet. On a pick, the full glasses are raised and brought towards each other, leaning in to the middle, faster and faster until the bowls touch: there three small gold rays flash, the glasses bounce back a little, stay close for a moment and go back down to their places; the knock and the stop set the wine swinging, and with glasses full to the brim they throw out a few drops. A single glass toasts on its own: it is raised, tips and goes back down. Below appears Noted in your cellar book., and at every change of mind Changed to followed by the new rating. A finger slides along the glasses and the rating falls where it lifts; with the keyboard the arrows pour at once and log the rating after a short pause, Enter logs it straight away.

How it works

Five glasses over five real radio buttons

Over the glasses lie five real radio buttons, see-through and with no pointer events, in a fieldset tied to the question (aria-labelledby): Tab goes into them, the arrows change the rating, screen readers read the number and the word (aria-label) and the form sends the value. The hand works on the glasses through pointer events; the arrows pour at once and log the rating after a 0.55 second pause (a gsap.delayedCall that every key restarts), Enter and Space log it straight away.

A glass drawn from a profile

The bowl comes from one function: its half width at every height, a quarter ellipse from the bottom up to the widest point (BELLY), then a cosine curve going in towards the rim. From it come the outline, the light on the glass (a line parallel to the edge), the inside cut by the thickness of the glass and used as a clipPath for the wine, and the rim as two half ellipses, the far one paler. Each glass is a group with a translate and a rotate round its foot, redrawn only when something changes.

Wine that stays level and sloshes

The wine’s surface is the sum of the first three sloshing modes of a bowl, cos(mπ(ξ+1)/2) across the width of the wine at its height, each a damped spring whose frequency grows as the square root of m (SLOSH). Every frame measures where the middle of the wine has got to and works out its acceleration: sideways it pushes the odd modes towards the slope a/g (the wine piles up away from where the glass goes), up and down it pushes the even one. In the glass’s own drawing the surface is tilted by −tan θ, so it stays level on the page however the glass leans. The sums run on GSAP’s ticker in steps of 1/240 of a second, with the frame capped at 1/30, and once everything is still the ticker is removed.

The rim and the drops

With the top rating the wine goes up to BRIM, near the rim. Every step checks the surface’s height at both sides of the mouth: whatever rises past the rim turns into drops, more the higher it goes, which leave the rim outwards with the glass’s speed, fall with gravity and vanish within half a second or on the table. They are tears drawn in SVG, pointed on the side they come from. The wine lost pays itself back: the level goes back to its spring.

A toast where the bowls touch

For the toast each full glass has a pose: towards the middle of the full glasses by TOAST, leaning by TILT degrees for each place away, raised by 8 pixels. Before it starts, the share of the way at which two neighbouring bowls really touch is found: the points of one outline are taken into the other glass’s drawing and checked against its profile. A GSAP timeline takes the glasses there speeding up (power2.in), fires the spark at the point of contact, bounces them back by a tenth, holds them close and sets them down. The wine knows nothing of the timeline: it swings only because the glass stops short.

Fingers, keys, the preview

A finger slides sideways along the glasses (touch-action: pan-y) and the rating falls where it lifts; a mouse button let go away from the glasses picks nothing. The glass under the pointer is held by a soft spring that lifts it by 2.5 pixels and leans it up to 3 degrees towards the pointer, and the hand lets go when the toast starts. A click from a script (detail 0, the library’s preview) is a pretend hand: it runs to the rating and picks in turn Unforgettable, Pleasant, Elegant and Simple.

Values to change

NameFileDefaultWhat it changes
LABELSscript.js['Not for me', 'Simple', 'Pleasant', 'Elegant', 'Unforgettable']The word for each rating, from the lowest to the highest (and how many glasses: 2 to 6).
STARTscript.js0The rating picked when the page opens (0 = empty glasses).
POURscript.js0.4How much wine is poured in a glass, from the bottom of the bowl (0) to the rim (1).
BRIMscript.js0.92How far they are poured for the top rating: so near the rim that the wine spills.
SLOSHscript.js[2.6, 0.055]The wine’s slosh: how many times a second it swings and how quickly it calms down.
TOASTscript.js0.62How far the glasses travel towards each other for the toast (with too little the bowls no longer touch and the toast goes without a spark).
TILTscript.js8How far the glasses lean in for the toast, in degrees for each place away from the middle.
STAGGERscript.js0.07The time between one glass and the next as they are poured or emptied, in seconds.

Try them in the Playground

When to use it

For rating a wine in a winery’s or wine shop’s app, for a wine club’s tasting notes, for reviewing a bottle bought online, to ask how a dinner or an evening at a wine bar went: wherever a rating is about drinking and celebrating, and stars would feel anonymous.

On phones

The glasses narrow with the screen: 358 pixels at 390. A finger slides sideways along the glasses and the rating falls where it lifts; up and down the page still scrolls (touch-action: pan-y). A tap picks straight away and starts the toast.

With reduced motion

No sloshing, no drops and no toast: the wine fades in already at its level and fades out the same way, the glasses stay still on the table; the word and the thanks change in a fade.

Pitfalls

  • The glasses’ first entrance was a GSAP tween on y: GSAP writes the SVG group’s transform and clearProps wiped it at the end, so all five glasses ended up on top of each other in the middle. Now the entrance is the hand’s spring: the glasses start 7 pixels higher and settle.
  • In the first toast the bowls touched for a single frame and bounced back by almost a third of the way: in the pictures they never seemed to meet. Now they bounce back by a tenth and stay close for a moment, and the touch shows.
  • The spark’s point was fixed, halfway between two glasses at the height of the bowl’s belly, and with the glasses leaning it fell inside the glass. Now it is the point of the outline that first goes into the other glass, from the same search that finds the contact.
  • With the top rating the toast threw out sixty drops that fell between the glasses and even out of the box: now the spill makes a few slow ones that vanish within half a second, on the table or at the edge of the box.
  • A single glass toasting on its own, leaning by 8 degrees and held by the pointer as well, seemed to be falling over: the pointer lets go of the glass when the toast starts and the glass alone leans only a little.
  • Leaving the glasses with the pointer right after the click cancelled the waiting toast: leaving now only brings back the rating picked, and the toast starts anyway.
  • With a low TOAST the bowls never touch: the contact search says so and the toast goes without a spark, instead of firing it in mid-air.

Libraries

GSAP 3.15.0, Cormorant (font variabile) 5.3.0.

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

Testing

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