
The slide-to-confirm button
Buttons, Physics, Transitions
NewFree
The order notifications of a home goods shop are brought by a small drone: it comes in from the side with the notification hanging from two cables, brakes while the notification swings, lowers it into its place, the cables go slack, the clips let go and the drone flies off reeling them in. Dismissed, another drone comes back for it, hooks the edge, takes the weight with a small dip and carries it away, while the next notification arrives.
A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React tab holds 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, viewport-fit=cover">
<title>The drone-drop toast</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/geist@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the order notifications of a home goods shop, brought by a small delivery drone. The drone flies in with the
notification hanging from two cables, lowers it into its place, lets go and flies off reeling the cables in.
Dismissed (the cross, Track, Esc), a drone comes back, hooks it and lifts it away, while the next one arrives;
swiped sideways, it is thrown off. The notifications and the drones are made by script.js, from MESSAGES;
the cables are chains of points worked out every frame, the notification is a rigid body hanging from them -->
<div class="dock" data-dock>
<section class="shelf" aria-label="Notifications" data-shelf></section>
<svg class="sky" aria-hidden="true" data-sky>
<defs>
<linearGradient id="drone-shell" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" style="stop-color: var(--drone-light)"/>
<stop offset="0.55" style="stop-color: var(--drone)"/>
<stop offset="1" style="stop-color: var(--drone-dark)"/>
</linearGradient>
<linearGradient id="drone-disc" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" style="stop-color: var(--rotor); stop-opacity: 0"/>
<stop offset="0.5" style="stop-color: var(--rotor); stop-opacity: 0.32"/>
<stop offset="1" style="stop-color: var(--rotor); stop-opacity: 0"/>
</linearGradient>
<clipPath id="drone-body-clip">
<rect x="-29" y="-13" width="58" height="23" rx="11.5"/>
</clipPath>
</defs>
<g data-cables></g>
<g data-drones></g>
</svg>
<p class="sr" role="status" aria-live="polite" aria-atomic="true" data-live></p>
</div>
</main>
<template data-template>
<article class="toast" inert>
<svg class="toast__icon" viewBox="0 0 36 36" aria-hidden="true" data-icon></svg>
<div class="toast__text">
<p class="toast__title" data-title></p>
<p class="toast__body" data-body></p>
</div>
<button class="toast__action" type="button" data-action></button>
<button class="toast__close" type="button" aria-label="Dismiss notification" data-close data-autoclic>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 7l10 10M17 7L7 17"/></svg>
</button>
</article>
</template>
<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.
43 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 drone-drop toast” from Animorio and fit it into my siteThe notification is a Verlet rigid body: its four corners are points with their position now and before, held together by six sticks (the four sides and the two diagonals). At every step gravity pulls them down and the constraints put them back, eight times; the centre and the angle come out of the points, and the real notification (an HTML element with its buttons) moves with a single transform. The physics step is never longer than 1/240 of a second, and a slow frame takes more of them.
Each cable is a one-way constraint: when the clip is further from the winch than the cable is long it is brought back, otherwise the cable is slack and does nothing. The clip holds a point on the top edge, between the two corners, so the correction is shared between the two corners by how close the point is to each (the weights (1 − a) and a, normalised). With the winches closer together than the clips the cables open into a trapezium: the notification hangs straight, but braking makes it swing and turn as well. The floor of its place stops the corners with friction, so it lands one corner first.
GSAP does not move the drone: it moves its target (cruise, then braking, then the climb and the exit) and the length of the cables, with calls to hook and let go. The drone follows it on a lightly damped spring, so it lags, goes a little past and comes back; it leans as much as it accelerates, filtered, like a real quadcopter. The cables' pull drags it down, and when it takes the weight it dips a little. The swing is damped against the drone and not against the air, or in flight the notification would always trail behind, crooked.
What shows of a cable is a chain of fourteen points, hung from the winch and the clip, with segments a thirteenth of the cable's length: taut it becomes a line, slack it bends, free it dangles with the heavier clip, and reeled in it gathers under the drone. A slack cable rests on the edge of the place and never crosses the words. Every frame the chain becomes an SVG path with Bézier curves through the midpoints, and the clip turns along the cable (or square to the edge, while it holds).
The drone is an SVG group: the shell with a gradient from grey to almost black, the orange band clipped to the shell, the visor, the arms, the legs and the winches. Each propeller is a blurred disc (a gradient fading at the sides) with a blade on top that widens and narrows with the cosine of its angle, like a spinning blade seen edge on. The drawing loop stops by itself when nothing is flying any more.
While it flies the notification is inert; once down the buttons work, a role="status" line reads it and, if the focus was on the dismissed notification, it moves to the new one's cross. Esc dismisses it. A sideways drag picks its direction after six pixels (vertically the page scrolls); let go, a throw becomes the speed and turn of the four points, which fall freely. LIFE, when above zero, sends the drone by itself, but the wait stops under the pointer and with the focus inside.
| Name | File | Default | What it changes |
|---|---|---|---|
FLY | script.js | 1.3 | The seconds the drone takes to fly in with a notification. |
LOWER | script.js | 0.9 | The seconds it takes to lower the notification into its place. |
COLLECT | script.js | 0.8 | The seconds a drone takes to come back for a dismissed notification. |
CABLE | script.js | 120 | How long the cables are while the drone carries the notification, in pixels. |
GRIP | script.js | 0.2 | Where the clips hold the top edge, from each end: 0.05 near the corners, 0.35 near the middle. |
TILT | script.js | 1 | How far the drone leans into its flight: 0 upright, 2 a lot. |
SWAY | script.js | 2 | How quickly a swing dies down: 0.3 it swings for long, 5 it barely swings. |
LIFE | script.js | 0 | The seconds before a drone collects a notification by itself (0: it stays until dismissed). |
FIRST | script.js | 0.5 | The seconds before the first notification arrives. |
MESSAGES | script.js | quattro, da Your order has shipped a Return picked up | The notifications, in turn: the parcel drawn (box, route, open, return), the title, the line under it and the button. |
For the notifications of an online shop or a delivery app, where the message really is about a parcel: shipped, out for delivery, delivered, returned. One notification at a time, with a title and a line; arriving takes about three seconds, so it suits places where notifications are few and matter, not where they come in bursts. On a quieter site FLY can be shorter and SWAY higher.
The notification takes the width of the screen minus 32 pixels (358 at 390) and below 420 pixels the gaps and the text get a little narrower; the clips stay a fifth from each end, so the cables open a little less. The drones come in and leave at the edges of the screen. A sideways swipe throws it off; vertically the page scrolls as usual. The cross has a touch area bigger than the drawn one.
No drones, no cables and no swinging: the notification fades in at its place and fades out, with the cross, with Track, with Esc or with a swipe, and the next notification fades in.
GSAP 3.15.0, Geist (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 12 KB; the libraries: GSAP 28 KB.
Tested on 13 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.