
Column shutter
Transitions
NewFree
A made-up magazine with big headlines and drawn illustrations: with a click, or taking the page by its corner, the page turns with a real curl. It folds along a line that follows the finger, shows its back, darkens towards the fold and throws its shadow on the page below. The pointer near a corner lifts it a little.
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 magazine to leaf through</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full-italic.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the magazine to leaf through: click a page or drag a corner and the page turns with a real curl,
its back showing, the fold shaded and its shadow falling on the page below -->
<section class="mag" aria-label="Loam, a magazine of slow places" data-mag>
<div class="book" tabindex="0" aria-roledescription="magazine" data-book>
<div class="base base--left" aria-hidden="true" data-base-left></div>
<div class="base base--right" aria-hidden="true" data-base-right></div>
<!-- the pages, in reading order: each sheet carries two of them, front and back -->
<article class="pg pg--cover" data-page data-autoclic data-name="Cover">
<p class="cover__mast" data-in>Loam</p>
<p class="cover__tag" data-in>A magazine of slow places</p>
<svg class="art" viewBox="0 0 100 134" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
<circle class="c-ochre" cx="76" cy="68" r="14" data-pop/>
<path class="c-olive" d="M0 96c18-10 34-12 52-6s34 4 48-4v48H0z"/>
<path class="c-terra" d="M0 108c22-8 40-6 58 0s30 6 42 0v26H0z"/>
<path class="c-ink" d="M86 102v-16" stroke-width="1.4"/>
<circle class="c-deep" cx="86" cy="81" r="7" data-pop/>
<circle class="c-deep" cx="81" cy="86" r="4.4" data-pop/>
<circle class="c-deep" cx="91" cy="86" r="4.8" data-pop/>
<path class="c-line" d="M56 50l3 2 3-2M64 44l2.4 1.6 2.4-1.6"/>
</svg>
<div class="cover__lines">
<p class="cover__issue" data-in>Autumn issue</p>
<p data-in>The orchard that outlived its town</p>
<p data-in>Bread at four in the morning</p>
<p data-in>Rivers, slowly</p>
</div>
</article>
<article class="pg" data-page data-name="Contents">
<p class="kicker" data-in>Inside</p>
<h2 class="title title--mid" data-in>This autumn, we stayed put</h2>
<div class="toc">
<div class="toc__row" data-in><p class="toc__sec">Feature</p><p class="toc__name">The orchard that outlived its town</p><p class="toc__what">Forty trees and the people who still pick them.</p></div>
<div class="toc__row" data-in><p class="toc__sec">Kitchen</p><p class="toc__name">Bread at four in the morning</p><p class="toc__what">A night with the last baker on the hill.</p></div>
<div class="toc__row" data-in><p class="toc__sec">Places</p><p class="toc__name">Rivers, slowly</p><p class="toc__what">Following water at walking pace.</p></div>
</div>
<svg class="leaf" viewBox="0 0 40 40" aria-hidden="true" data-pop><path class="c-terra" d="M8 32C8 16 18 8 34 6c0 16-8 26-26 26z"/><path class="c-line" d="M8 32L26 14"/></svg>
</article>
<article class="pg" data-page data-autoclic data-name="Letter">
<p class="kicker" data-in>From the editors</p>
<h2 class="title title--big" data-in>Go slower than the weather</h2>
<div class="body body--cols" data-in>
<p class="drop">We meant to travel far this season. Instead we walked to the end of the lane and found more than we could write about. An orchard that nobody owns and everybody tends. A bakery whose lights come on before the birds. A river that takes a whole afternoon to cross a valley.</p>
<p>None of it is new, and that is the point. The places in this issue have kept their own time while the rest of us hurried. We sat with them long enough to hear it.</p>
<p>Read it slowly. Turn the pages by the corner, the way paper likes to be turned.</p>
</div>
<p class="sign" data-in>The editors</p>
</article>
<article class="pg pg--art" data-page data-name="The orchard">
<svg class="art" viewBox="0 0 100 134" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
<rect class="c-sky" width="100" height="134"/>
<circle class="c-ochre" cx="78" cy="22" r="9" data-pop/>
<path class="c-olive" d="M0 70c20-6 40-6 60-2s30 2 40-2v68H0z"/>
<path class="c-ink" d="M22 96V78M52 92V72M80 98V80" stroke-width="2"/>
<circle class="c-deep" cx="22" cy="70" r="12" data-pop/>
<circle class="c-deep" cx="52" cy="64" r="13" data-pop/>
<circle class="c-deep" cx="80" cy="72" r="11" data-pop/>
<g class="c-terra">
<circle cx="17" cy="66" r="1.8" data-pop/><circle cx="26" cy="73" r="1.8" data-pop/><circle cx="21" cy="62" r="1.6" data-pop/>
<circle cx="47" cy="60" r="1.8" data-pop/><circle cx="57" cy="66" r="1.9" data-pop/><circle cx="50" cy="70" r="1.6" data-pop/>
<circle cx="76" cy="69" r="1.7" data-pop/><circle cx="85" cy="74" r="1.8" data-pop/>
</g>
<path class="c-ochre" d="M0 104c30-6 60-6 100 0v30H0z"/>
<path class="c-line" d="M60 120l8-36M66 120l8-36M61 112h6M62 104h6.6M64 96h6.6M65.6 88h6.6"/>
<path class="c-terra" d="M30 112h14l-2 9H32z" data-pop/>
</svg>
<p class="caption" data-in>Picking season, early October</p>
</article>
<article class="pg" data-page data-autoclic data-name="The orchard">
<p class="kicker" data-in>Feature</p>
<h2 class="title title--huge" data-in>The orchard that outlived its town</h2>
<p class="stand" data-in>The factory closed, the school closed, the bus stopped coming. The apple trees kept going.</p>
<div class="body body--cols" data-in>
<p>Nobody remembers who planted the first row. The deeds were lost in a flood, and the land has belonged to whoever turns up with a ladder ever since.</p>
<p>Every October they do. A retired teacher, two brothers from the coast, a family who drive four hours with empty crates. They prune, they argue about which tree is sweetest, they leave a basket by the gate for anyone passing.</p>
</div>
<blockquote class="quote" data-in>Nobody owns it, so everybody looks after it.</blockquote>
</article>
<article class="pg" data-page data-name="Bread at four">
<p class="kicker" data-in>Kitchen</p>
<h2 class="title title--big" data-in>Bread at four in the morning</h2>
<div class="body body--cols" data-in>
<p class="drop">The oven is older than the house around it. It takes three hours to warm, so the baker lights it at one and sleeps on a bench until the bricks tick.</p>
<p>By four the dough has doubled in its linen. She shapes forty loaves by feel, in the dark, and the first customers knock before the crust has stopped singing.</p>
</div>
<div class="box" data-in>
<p class="box__head">You need</p>
<p>Flour, water, salt, and a whole night of patience.</p>
</div>
</article>
<article class="pg pg--art pg--night" data-page data-autoclic data-name="Bread at four">
<svg class="art" viewBox="0 0 100 134" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
<rect class="c-night" width="100" height="134"/>
<circle class="c-paper" cx="74" cy="24" r="8" data-pop/>
<circle class="c-night" cx="78" cy="21" r="7"/>
<path class="c-brick" d="M14 112V74a36 30 0 0 1 72 0v38z"/>
<path class="c-glow" d="M30 112V88a20 16 0 0 1 40 0v24z" data-pop/>
<path class="c-line-light" d="M14 84h72M14 96h16M70 96h16M22 74v10M78 74v10M50 44v14"/>
<ellipse class="c-loaf" cx="34" cy="120" rx="10" ry="5" data-pop/>
<ellipse class="c-loaf" cx="56" cy="121" rx="11" ry="5.4" data-pop/>
<ellipse class="c-loaf" cx="76" cy="119" rx="9" ry="4.6" data-pop/>
<path class="c-line-light" d="M30 118l3-2M53 119l3-2M73 117l3-2"/>
<path class="c-steam" d="M44 40c-3-4 3-6 0-10s3-6 0-10M56 40c-3-4 3-6 0-10s3-6 0-10" data-pop/>
</svg>
<p class="caption caption--light" data-in>The oven, lit at one</p>
</article>
<article class="pg pg--art pg--river-left" data-page data-name="Rivers, slowly">
<svg class="art" viewBox="0 0 100 134" preserveAspectRatio="xMaxYMid slice" aria-hidden="true">
<rect class="c-sky" width="100" height="134"/>
<path class="c-olive" d="M0 74c24-10 50-8 76-2s20 4 24 4v58H0z"/>
<path class="c-river" d="M0 100c18-4 34 6 52 2s34-14 48-10v42H0z"/>
<path class="c-line-light" d="M8 110c6-2 10 0 16-1M36 118c6-2 10 0 16-1M60 104c6-2 10 0 14-1"/>
<path class="c-deep" d="M14 76l5-14 5 14zM28 72l4-11 4 11z" data-pop/>
</svg>
<p class="kicker kicker--over" data-in>Places</p>
<h2 class="title title--huge title--over" data-in>Rivers, slowly</h2>
</article>
<article class="pg pg--art pg--river-right" data-page data-autoclic data-name="Rivers, slowly">
<svg class="art" viewBox="0 0 100 134" preserveAspectRatio="xMinYMid slice" aria-hidden="true">
<rect class="c-sky" width="100" height="134"/>
<circle class="c-ochre" cx="70" cy="40" r="10" data-pop/>
<path class="c-olive" d="M0 76c10 0 30-6 50-4s34 6 50 2v60H0z"/>
<path class="c-river" d="M0 92c14-4 28 4 44 6s36-2 56 2v34H0z"/>
<path class="c-line-light" d="M10 104c6-2 10 0 16-1M40 112c6-2 10 0 16-1M70 106c6-2 10 0 14-1"/>
<path class="c-terra" d="M30 96h18l-4 4H34z" data-pop/>
<path class="c-ink" d="M39 96V84" stroke-width="0.8"/>
</svg>
<div class="card" data-in>
<p>We walked the river from its spring to the sea, never faster than the water. It took eleven days. The best hour was the one we spent doing nothing on a stone.</p>
</div>
</article>
<article class="pg pg--back" data-page data-name="Back cover">
<p class="back__mast" data-in>Loam</p>
<svg class="art art--small" viewBox="0 0 100 70" aria-hidden="true">
<path class="c-paper" d="M0 50c24-12 48-14 72-6s20 4 28 2v24H0z"/>
<path class="c-ink" d="M58 44v-8" stroke-width="1.2"/>
<path class="c-paper" d="M51 38l7-14 7 14z" data-pop/>
<circle class="c-paper" cx="24" cy="18" r="2" data-pop/><circle class="c-paper" cx="40" cy="10" r="1.4" data-pop/><circle class="c-paper" cx="80" cy="16" r="1.8" data-pop/>
</svg>
<p class="back__next" data-in>Next: Winter, the quiet issue</p>
<svg class="code" viewBox="0 0 40 16" aria-hidden="true"><path d="M2 0v16M4 0v16M7 0v16M8.4 0v16M11 0v16M14 0v16M15.4 0v16M18 0v16M21 0v16M22.4 0v16M25 0v16M27 0v16M30 0v16M31.4 0v16M34 0v16M37 0v16M38.4 0v16"/></svg>
</article>
</div>
<p class="where" aria-hidden="true"><span class="where__roll" data-where>Cover</span></p>
<p class="hint">Click a page, or drag it by the corner</p>
<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.
65 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 magazine to leaf through” from Animorio and fit it into my siteThe page does not swing round the spine like a door: it folds. The corner C is brought to the point P (where the finger is), and the fold is the bisector of the segment CP: the line through its midpoint, perpendicular to CP. Everything is worked out as if the sheet lay on the right; for a sheet on the left the x values are mirrored (2W − x). Two limits keep the paper whole: the corner stays within a page's width of the spine on its own edge, and within a diagonal of the spine on the opposite edge.
Every sheet has two pages, front and back, each in its own layer as big as the open magazine. The face that stays down is clipped with clip-path: the page's rectangle cut by the fold (the Sutherland-Hodgman algorithm against a half-plane). The folded face is clipped to the other part and moved with a matrix: each of its points goes onto the sheet, then is mirrored across the fold. The matrix comes from three points, (0,0), (1,0) and (0,1), and is written as matrix() with transform-origin 0 0.
The paper looks curved thanks to three soft bands laid along the fold: a dark one on the face that stays down (the paper rising loses light), one on the folded back with a narrow shade, a bright highlight and then a dark veil towards the corner, and one on the page below, in the part being uncovered. Each band is a long div, turned with atan2 and laid with one edge on the fold; the clipped face trims it by itself. They grow with the fold and fade as the page lies down on the other side. The folded back also has a drop-shadow, on the layer that holds it: on the page itself the clip-path would cut it off.
A click takes the corner from C to the opposite corner (0, cy) with a GSAP tween: x goes straight, y rises in an arc (the sine of the run times CURL) towards the middle of the page, so the corner draws a curve. Dragging, P follows the pointer with a 0.22 second tween; let go, the corner's place and speed decide: past the middle, or faster than FLICK, it finishes turning, otherwise it goes back. Near a corner the pointer lifts it by LIFT pixels and an elastic.out puts it down again.
The order of the pages is rebuilt after every turn: on the right the sheet with the lowest index is on top, on the left the highest; the turning sheet goes above all of them, and its folded back above its own face. Closed, the magazine sits in the middle (half a page's x); open, the spine does. The edges of the pages below are shadows, one line per sheet. The preview's click (detail 0) turns the next page; at the last one it closes the magazine with the sheets in sequence, RIFFLE seconds apart.
| Name | File | Default | What it changes |
|---|---|---|---|
TURN | script.js | 1.1 | How many seconds a page takes to turn after a click. |
CURL | script.js | 0.4 | How high the corner rises while the page turns, as a share of the page's height: 0 turns it close to the edge, 0.8 lifts it in a high arc. |
SHADOW | script.js | 0.7 | How dark the shadows on the fold and on the page below are (from 0 to 1). |
LIFT | script.js | 26 | How many pixels the corner lifts when the pointer comes near it (0 = it does not lift). |
CORNER | script.js | 0.3 | How near the corner the pointer has to come to lift it, as a share of the page's width. |
FLICK | script.js | 650 | The speed, in pixels a second, at which a dragged page turns even before the middle. |
RIFFLE | script.js | 0.16 | The seconds between one page and the next when, at the end, the magazine closes again. |
To leaf through something born on paper: an online magazine or catalogue, a print portfolio, a restaurant menu, a children's book, a lookbook, an annual report read page by page, an invitation. It works best with pages that read on their own, big headlines and full-page pictures.
The same magazine with the same folds, open on two pages as wide as the screen: the sizes inside the pages are shares of the width, so the headlines stay big and the text gets small, like a real magazine seen from a distance. It turns with a tap or by dragging the corner with a finger; the lift under the pointer is not there, because a touch has no pointer coming near.
Nothing folds and nothing moves: at a click the magazine fades out and comes back open on the next page, already in place; closing at the end is a fade too. On arrival it fades in, with no bounce and no corner lifting, and the words do not rise.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Instrument Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 13 KB; the libraries: GSAP 28 KB.
Tested on 10 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.