
The layered button
Buttons, Hover, Physics
Free
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.
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>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
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 siteEach 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.
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 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.
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.
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.
| Name | File | Default | What it changes |
|---|---|---|---|
OVERLAP | script.js | 14 | How many pixels each face hides under the next one. |
SPREAD | script.js | 22 | How many pixels the faces near the pointer draw apart. |
REACH | script.js | 64 | How far, in pixels, the pointer’s pull reaches. |
SPRING | script.js | [360, 0.68] | The faces’ spring: how stiff it is and how much it is held back. |
SHOWN | script.js | 5 | How many faces show before the +N. |
PEOPLE | script.js | [['Ana Ruiz', '#f4a67c'], …] | The people going, each with the colour of their face. |
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.
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.
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.
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.
Tested on 3 October 2026
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.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.