Skip to content
animorio
IT
All components

The avatar group

Avatars, Physics, HoverGSAPMediumCompressed weight: Demo 7.1 KBGSAP 28 KBFreeDownload the zip

The faces of the people going to a dinner, overlapping: near the pointer they draw apart on springs and the one under it rises a little with its name; the +4 opens into the faces it was hiding, which come out of it one after another, and Join drops a new face into the group with a bounce.

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 avatar group</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/kumbh-sans@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the avatar group: the faces draw apart around the pointer on springs, the one under it rises with its name,
         the +4 opens into the faces it hid, and joining drops a new face into the group -->
    <section class="going" aria-labelledby="going-title" data-going data-autoclic>
      <h2 class="going__title" id="going-title">Friday dinner</h2>
      <p class="going__where">Trattoria del Ponte · 8 pm</p>
      <div class="going__stack" data-stack>
        <ul class="going__list" aria-label="Who is going" data-list></ul>
        <button class="going__more" type="button" aria-expanded="false" data-more>
          <span class="going__more-text" data-more-text></span>
          <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 12h10"/></svg>
        </button>
      </div>
      <div class="going__foot">
        <p class="going__count"><span class="going__num" data-num></span> going</p>
        <button class="going__join" type="button" data-join><span data-join-text>Join</span></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.

35 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 avatar group” from Animorio and fit it into my site

What it does

On a black background a dark card with Friday dinner and Trattoria del Ponte · 8 pm; under it, five coloured circles with initials, overlapping, and a grey circle +4; at the bottom 9 going and a lime button Join. When the page opens the faces fan out from under the first one. Going over them with the pointer, the nearby faces draw apart on springs, more the closer they are, and the one under the pointer rises and grows a little, showing its name underneath. The +4 opens: the four hidden faces come out of it one after another, growing, while the circle slides to the end of the row and becomes a minus sign; pressing it again they go back in. Join drops from above, in front of all, a lime face with You, with a bounce, while the others move over and the number rolls up to 10; the button becomes Leave, which lets the face fall away and closes the row up.

How it works

The places

Each face has its place in the row: a step of 46 − OVERLAP pixels from the one before, or less if the row would not fit (with all the faces, on a phone, they close up). Their x, their height and their scale go to their places on springs (SPRING), worked out on GSAP’s ticker in steps of 4 milliseconds; the first face sits on top of the others.

The pointer that spreads

Every space between two faces grows by SPREAD × e^(−(d ÷ REACH)²), where d is the distance between the pointer and the middle of the space: the faces near the pointer draw further apart. The nearest face, measured on the resting places (so it does not run away), rises by 6 pixels and grows to 1.12, and its name shows underneath. Over the +4 the row does not spread, so the +4 stays still under the pointer.

The +N that opens

The faces after the first SHOWN wait inside the +N circle, small and clear. Opening it, they come out one every 0.07 seconds, growing from 0.4 to 1 on their springs, while the circle follows the row to its end and the number makes way for a minus sign; closing it, they go back into the circle and fade.

Join and Leave

The new face starts 70 pixels higher, tilted, and drops into the first place on a softer spring that bounces; the others move over by a step. The number rolls: the old one leaves the way of the change and the new one comes from the other side (with immediateRender false, so the old one really leaves). Leave lets the face fall by 36 pixels, turning and fading.

Keys and the preview

The faces are a list with a single stop for Tab: the arrows (and Home and End) go from face to face, and the one with the focus spreads the others and shows its name as the pointer would; the +N is a button with aria-expanded, and an aria-live region says who is going and when the list opens. On a phone a tap on a face lifts it with its name. A click from a script (the library’s preview) is a pretend pointer that runs along the faces, opens the +N, runs along them again, joins the group, closes it and leaves.

Values to change

NameFileDefaultWhat it changes
OVERLAPscript.js14How many pixels each face hides under the next one.
SPREADscript.js22How many pixels the faces near the pointer draw apart.
REACHscript.js64How far, in pixels, the pointer’s pull reaches.
SPRINGscript.js[360, 0.68]The faces’ spring: how stiff it is and how much it is held back.
SHOWNscript.js5How many faces show before the +N.
PEOPLEscript.js[['Ana Ruiz', '#f4a67c'], …]The people going, each with the colour of their face.

Try them in the Playground

When to use it

For the people coming to an event, the members of a project or a group, who is looking at a document, the guests of a booking, the customers who left a review.

On phones

The row closes up to fit the width of the screen, even with all the faces open; a tap on a face lifts it and shows its name until the finger lifts.

With reduced motion

The faces go straight to their places, without springs: they spread, open and close at once, the new face appears in its place without dropping, the number changes without rolling and the name shows without fading.

Pitfalls

  • The face to lift must be chosen by the resting places, not the spread ones: otherwise, as it spreads, the face runs away from the pointer and the choice jumps from face to face.
  • An element that moves away as the pointer comes near is hard to press: over the +N the row stops spreading, so it stays still.
  • The faces’ z-indexes also count against the +N, because the list has no stacking context of its own: the +N has a higher z-index, so the faces come out from under it.
  • For the keys, a single Tab stop on the list (a moving tabindex) and the arrows inside: nine stops one after another would be too many.

Libraries

GSAP 3.15.0, Kumbh Sans (font variabile) 5.3.0.

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

Testing

Tested on 3 October 2026

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

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