
The loader that becomes a logo
Transitions, SVG, Text
NewFree
A page change on click: the page breaks into real jigsaw pieces, tabs and all, that lift, spin and fly away; the pieces of the new page come in from the sides, turn over and click into place, and the last one drops in 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 jigsaw transition</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- two pages of a made-up studio. Click a link and the page breaks into jigsaw pieces that lift, spin and fly
away; the pieces of the new page come in from the sides, turn over and click into place, and the last one
drops in with a bounce -->
<div class="palco" data-palco>
<!-- the studio page -->
<section class="pagina pagina--studio" data-pagina="studio" aria-label="Studio">
<header class="testa">
<a class="marchio" href="#studio" data-vai="studio" aria-label="Mortise, studio page">
<svg class="marchio__segno" viewBox="0 0 32 32" aria-hidden="true">
<path d="M3 3h11v3.2a3 3 0 1 0 4 0V3h11v11h-3.2a3 3 0 1 0 0 4H29v11H18v-3.2a3 3 0 1 0-4 0V29H3V18h3.2a3 3 0 1 0 0-4H3z"/>
</svg>
<span>Mortise</span>
</a>
<nav class="voci" aria-label="Main">
<a href="#studio" data-vai="studio" aria-current="page">Studio</a>
<a href="#work" data-vai="work">Work</a>
</nav>
<span class="testa__posta">hello@mortise.studio</span>
</header>
<main class="studio">
<div class="studio__testo">
<p class="etichetta" data-onda>Independent design studio</p>
<h1 class="titolo" tabindex="-1" data-onda>We make the pieces fit.</h1>
<p class="studio__frase" data-onda>Identities, websites and motion for people who build things carefully. Twelve of us, one long table.</p>
<a class="tessera" href="#work" data-vai="work" data-autoclic data-onda>
<svg class="tessera__forma" aria-hidden="true"><path data-forma/></svg>
<span class="tessera__testo">See the work</span>
<svg class="tessera__freccia" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12h15M13 6l6 6-6 6"/></svg>
</a>
</div>
<div class="composizione" aria-hidden="true" data-onda>
<span class="forma forma--sole"></span>
<span class="forma forma--arco"></span>
<span class="forma forma--righe"></span>
<span class="forma forma--quarto"></span>
<span class="forma forma--blocco"></span>
</div>
</main>
<footer class="piede">
<span>Brand, web, motion</span>
<span>Booking projects for spring</span>
</footer>
</section>
<!-- the work page -->
<section class="pagina pagina--work" data-pagina="work" aria-label="Work" inert>
<header class="testa">
<a class="marchio" href="#studio" data-vai="studio" aria-label="Mortise, studio page">
<svg class="marchio__segno" viewBox="0 0 32 32" aria-hidden="true">
<path d="M3 3h11v3.2a3 3 0 1 0 4 0V3h11v11h-3.2a3 3 0 1 0 0 4H29v11H18v-3.2a3 3 0 1 0-4 0V29H3V18h3.2a3 3 0 1 0 0-4H3z"/>
</svg>
<span>Mortise</span>
</a>
<nav class="voci" aria-label="Main">
<a href="#studio" data-vai="studio">Studio</a>
<a href="#work" data-vai="work" aria-current="page">Work</a>
</nav>
<span class="testa__posta">hello@mortise.studio</span>
</header>
<main class="lavori">
<div class="lavori__testa">
<div>
<p class="etichetta" data-onda>Selected work</p>
<h1 class="titolo" tabindex="-1" data-onda>Four recent fits.</h1>
</div>
<a class="tessera" href="#studio" data-vai="studio" data-autoclic data-onda>
<svg class="tessera__forma" aria-hidden="true"><path data-forma/></svg>
<span class="tessera__testo">Meet the studio</span>
<svg class="tessera__freccia" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12h15M13 6l6 6-6 6"/></svg>
</a>
</div>
<ul class="griglia">
<li class="lavoro" data-onda>
<div class="copertina copertina--te" aria-hidden="true"><span></span><span></span></div>
<p class="lavoro__nome">Halden Tea</p>
<p class="lavoro__tipo">Identity</p>
</li>
<li class="lavoro" data-onda>
<div class="copertina copertina--bici" aria-hidden="true"><span></span><span></span></div>
<p class="lavoro__nome">Orbit Bikes</p>
<p class="lavoro__tipo">Website</p>
</li>
<li class="lavoro" data-onda>
<div class="copertina copertina--libri" aria-hidden="true"><span></span><span></span><span></span></div>
<p class="lavoro__nome">Paper Moon Press</p>
<p class="lavoro__tipo">Books</p>
</li>
<li class="lavoro" data-onda>
<div class="copertina copertina--radio" aria-hidden="true"><span></span><span></span></div>
<p class="lavoro__nome">Lowtide Radio</p>
<p class="lavoro__tipo">Motion</p>
</li>
</ul>
</main>
<footer class="piede">
<span>Brand, web, motion</span>
<span>Booking projects for spring</span>
</footer>
</section>
<!-- the table under the pages: the pieces fly over it -->
<div class="tavolo" data-tavolo aria-hidden="true" inert>
<svg class="tavolo__traccia" data-traccia></svg>
<div class="tavolo__pezzi" data-pezzi></div>
</div>
</div>
<p class="sr" role="status" aria-live="polite" data-avviso></p>
<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.
48 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 jigsaw transition” from Animorio and fit it into my siteOn a click the script copies the page (cloneNode) once per piece and cuts each copy with clip-path: path() along the outline of the piece. The copy is as big as the screen and shifted back by the position of the piece, so inside the cut you see exactly that bit of page, words and shapes included. The copies lose data-autoclic, data-vai and tabindex, and the table holding them is inert and aria-hidden: they cannot be clicked or read twice.
Every inner edge is made of ten points: a start and three cubic curves (shoulder and neck, head, neck and shoulder). The sizes of the tab are shares of the short side of a piece (KNOB), a little crooked at random (WOBBLE), and flip chooses which way it sticks out. Every edge is worked out once: the piece above walks it backwards (just reverse the list of points), so the two pieces match to the pixel. On a tall screen columns and rows swap.
Every piece has an SVG with its outline, two paths (a shade and a light) with pathLength="1": the cut draws itself with stroke-dashoffset from 1 to 0, first near the clicked link and then further away. Then the piece lifts (scale LIFT, a wider drop-shadow, back.out) and flies off along the direction from the link to its centre with power2.in, turning up to SPIN degrees. The pivot is the centre of the cell, not of the box with the tabs.
The new pieces start off screen with a negative scaleX: a piece upside down. A cardboard layer inside the cut turns on while scaleX is below zero (checked by the timeline's onUpdate with gsap.getProperty), so as the piece turns over you see the back first and then the page. Once it is seven pixels above its place, the piece drops with a power3.in keyframe to scale 0.975, the shadow goes out, and it comes back to 1 with elastic.out: that is the click of the fit.
The piece nearest the middle comes last, from above: it starts big (DROP, that is close to us) and crooked, and its scale goes back to 1 with bounce.out. The first contact of bounce.out falls at 1/2.75 of the duration: that is when the table shakes (keyframes on x and y) and the neighbours hop, the nearest first. At the end the cuts fade and in the same frame the real page is shown and the table is emptied: the pieces are already exact to the pixel, so the swap cannot be seen.
| Name | File | Default | What it changes |
|---|---|---|---|
COLUMNS | script.js | 4 | How many pieces across on a wide screen (on a tall screen columns and rows swap). |
ROWS | script.js | 3 | How many pieces down. |
KNOB | script.js | 0.1 | The size of the tabs, as a share of the short side of a piece: 0.1 is the classic jigsaw, 0.05 small tabs, 0.13 big ones. |
WOBBLE | script.js | 0.035 | How irregular the tabs are, so that no two are the same (0 = all perfect). |
SPIN | script.js | 70 | The most a piece turns in flight, in degrees. |
LIFT | script.js | 1.08 | How much a piece grows when it comes off the table. |
DROP | script.js | 1.7 | How high the last piece falls from: its size at the start of the fall. |
SPEED | script.js | 1 | The speed of the whole transition: 2 is twice as fast, 0.5 half as fast. |
For the page change of a playful site: a creative studio, a portfolio, a games and puzzles shop, an exhibition, a campaign about pieces fitting together (teams, partnerships, modular products). It also works between the sections of a single page or the steps of a presentation. Use it where the change is a small event, not on every click of an app.
The same transition with the sizes adapted: the tall screen is cut into three columns and four rows, so the pieces stay nearly square, and the work page puts the projects two to a row. A tap on the link is enough.
No pieces and no movement: the old page fades out and the new one fades in. On opening, the first page appears the same way.
GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 11 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.