
The cube stories
Stories, 3D, Transitions
NewFree
The notification of a shared-documents app made of real paper, with its grain and a soft shadow. Dismissed, it does not vanish: it folds itself into a paper plane, fold after fold, and the plane flies away on a curve while its shadow falls behind on the page. The next notification arrives as a folded sheet and opens; under the pointer the nearest corner curls up.
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, viewport-fit=cover">
<title>The paper notification</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/hanken-grotesk@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage" data-stage>
<!-- the notification of a shared-documents app, made of real paper. Dismissed (the cross, Esc, or a swipe
sideways) it folds itself into a paper plane, fold after fold, and the plane flies away; the next one arrives
as a folded sheet and opens. Open lifts the sheet towards you. The words are filled in by script.js, from
MESSAGES; while it folds, the paper is drawn by copies of the card, cut along the creases and turned in 3D -->
<canvas class="shadow" aria-hidden="true" data-shadow></canvas>
<div class="note" data-note>
<div class="note__mover" data-mover>
<section class="card is-hidden" aria-label="Notification" data-card>
<span class="card__face" aria-hidden="true" data-face></span>
<span class="card__text">
<span class="card__title" data-title></span>
<span class="card__body" data-body></span>
</span>
<button class="card__open" type="button" data-open>Open</button>
<button class="card__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>
</section>
<canvas class="cast" aria-hidden="true" data-cast></canvas>
<div class="folds" aria-hidden="true" inert data-folds></div>
</div>
<p class="sr" role="status" aria-live="polite" aria-atomic="true" data-live></p>
</div>
</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.
36 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 paper notification” from Animorio and fit it into my siteThe paper is a list of flat faces, each a piece of the notification cut along the creases made so far. A fold is a line on the paper as it lies at that moment, and the side to turn: every face the line crosses, on every layer, is cut in two (the cut is worked out where the face lies now, but the pieces are written in the coordinates of the flat notification), and the pieces on that side turn 180 degrees about the line. The axis lies half a paper's thickness above the stack (below it, for a fold backwards): a layer turned over lands on top of the others in the right order, as with real paper.
The folds are worked out once, all the way. Each piece remembers its folds as axes carried back onto the flat notification: the axis of a fold, as seen in the world once the folds before it are done, is carried back by the inverse of the turns the piece has already made. So any moment is just the product of that piece's turns (Rodrigues' formula), from the first to the last, at the angle GSAP has reached; folds may even overlap in time, and the plane's keel opens by 26 degrees simply by taking some angle off the fold in half.
Each face is a copy of the notification (with no id, data- or aria- attributes, inside an inert holder), cut with the clip-path of its piece, grown by half a pixel on every side so that no seam is left between two pieces, and turned with a matrix3d. The holder has the perspective but no preserve-3d: the script decides the order. A face is in front of another when all its corners lie on the eye's side of the other's plane (or the other way round); the pairs that overlap on screen make a graph, and the faces come out from the farthest of the free ones.
Each face has a veil: darker the further its normal turns from the lamp compared with the flat sheet, lighter as it turns towards it. The front or the back shows depending on the eye's side; the back is paper with the print barely showing through, and the outside of the folded letter is the back of the right third, turned over and so mirrored. The shadow lies on a canvas under the paper: every face is cast onto the page along the light, in three layers from the sharpest to the softest, and the higher it rises the further the shadow moves, spreads and fades.
The corner near the pointer is made of six strips, each hinged on the one before, with the curl growing towards the tip (up to CURL degrees) and a spring with a little bounce; the real notification is cut away right there. Each strip has a gradient from the light of the strip before to the light of the strip after, so the strips read as one curve, and a second canvas between the paper and the faces takes the corner's shadow on the paper itself.
Once folded, the plane leans to show its wings, lifts and follows a cubic Bézier curve in 3D: the nose follows the tangent, climbs a little and leans into the turn as much as the heading changes. A sideways swipe past a third of the width (or a fast one) sends it off that way, with its nose folded towards that side. The buttons are real, with a visible focus; Esc dismisses, the focus stays on the cross for the next notification, and a role="status" line reads out every new one.
| Name | File | Default | What it changes |
|---|---|---|---|
FOLD | script.js | 1.5 | The seconds the notification takes to fold itself into a paper plane. |
FLIGHT | script.js | 1.2 | The seconds the plane takes to fly away. |
UNFOLD | script.js | 0.85 | The seconds a new notification takes to arrive folded and open. |
CURL | script.js | 80 | How far the corner by the pointer curls at most, in degrees: a third far from the corner, all of it close to it. |
LIGHT | script.js | 0.8 | How much the light changes on a face as it turns: 0 none, 1 strong. |
GRAIN | script.js | 0.07 | The grain of the paper: 0 smooth, 0.2 rough. |
KEEL | script.js | 0.3 | How deep the plane's keel is, against half the height of the sheet: lower means wider wings. |
DEPTH | script.js | 900 | How far away the eye is, in pixels: less looks deeper. |
FIRST | script.js | 0.4 | The seconds before the first notification arrives. |
MESSAGES | script.js | quattro, da Mira Okafor “Q4 roadmap” a Jonas Weber “Hiring 2027” | The shared documents, in turn: who, the document, the line under it and the colour behind the initials. |
For a notification that brings something concrete, like a shared document, an invitation, a letter, and is closed with a gesture: a documents app, a mailbox, a workspace with a handmade touch. One notification at a time, with a line of title and one of detail; the folding takes a second and a half, so it suits places where notifications do not come in bursts.
The notification takes the width of the screen minus 32 pixels (358 at 390), and below 420 pixels the gaps and the title get a little narrower so that every word fits. A sideways swipe dismisses it: the plane has its nose towards that side and flies away that way, and the sheet comes back onto the screen a little as it folds. Holding a finger on the paper lifts the nearest corner; the cross has a touch area bigger than the drawn one.
No folds, no flight and no curling corners: the notification fades in and out, with the cross, with Esc, with Open or with a swipe, and the next notification fades in. The grain, the shadow and the two faint creases stay.
GSAP 3.15.0, Hanken Grotesk (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 18 KB; the libraries: GSAP 28 KB.
Tested on 6 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.