
The loader that becomes a logo
Transitions, SVG, Text
NewFree
A minimal page change: the words the two pages have in common never leave. On a click the others fade in a wave, and the ones that stay fly one after the other to their place on the new page, growing or shrinking and changing weight; they land on a thin line, take a touch of weight from the knock and give it back, then the new words rise and the red full stop drops in last.
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 words-that-stay transition</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/roboto-flex@5.3.0/standard.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage" data-stage>
<!-- the header is the same on both pages, so it never moves -->
<header class="bar">
<span class="brand">Quarra</span>
<nav class="nav" aria-label="Pages">
<a href="#index" data-go="a" data-nav="a" aria-current="page">Index</a>
<a href="#rooms" data-go="b" data-nav="b">Rooms</a>
</nav>
</header>
<!-- the first page. The words marked with the same data-share on both pages are the ones that stay:
they fly to their place on the other page, changing size and weight -->
<section class="page page--a is-on" data-page="a" aria-label="Index">
<div class="col col--a">
<h1 class="headline" data-words><span data-share="quiet">Quiet</span> <span data-share="rooms">rooms</span><br>for loud cities<span class="dot" data-share="dot">.</span></h1>
<p class="lede" data-words>An architecture studio in <span data-share="porto">Porto</span>: <span data-share="twelve">twelve</span> <span data-share="houses">houses</span>, one school, no towers.</p>
<a class="go go--next" href="#rooms" data-go="b" data-autoclic data-unit>
<span class="go__text">See the rooms</span>
<span class="go__arrow" aria-hidden="true"><span class="go__shaft"></span><svg class="go__head" viewBox="0 0 8 12"><path d="M1.5 1.5 6.5 6l-5 4.5"/></svg></span>
</a>
</div>
</section>
<!-- the second page -->
<section class="page page--b" data-page="b" aria-label="Rooms" aria-hidden="true">
<div class="col col--b">
<p class="sub" data-words><span data-share="quiet">Quiet</span> <span data-share="houses">houses</span>, <span data-share="twelve">twelve</span> of them.</p>
<p class="lede" data-words>Each one built in <span data-share="porto">Porto</span> around a single window, and a room left empty on purpose.</p>
<a class="go go--back" href="#index" data-go="a" data-autoclic data-unit>
<span class="go__arrow" aria-hidden="true"><svg class="go__head" viewBox="0 0 8 12"><path d="M6.5 1.5 1.5 6l5 4.5"/></svg><span class="go__shaft"></span></span>
<span class="go__text">Back to the index</span>
</a>
</div>
<h2 class="title" data-words><span data-share="rooms">rooms</span><span class="dot" data-share="dot">.</span></h2>
</section>
<footer class="foot">
<span>Since 2009</span>
<span>Visits by appointment</span>
</footer>
<!-- the words in flight and the baselines they land on (built and moved by the script) -->
<div class="fly" data-fly aria-hidden="true"></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.
39 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 words-that-stay transition” from Animorio and fit it into my siteIn the HTML the words that stay are marked with the same data-share on both pages (data-share="rooms" in the first page's headline and in the second page's title). The script splits everything with data-words into words: each word becomes an inline-block span, so it can move without pushing its neighbours, while the spaces stay text and the lines break as before. Words without a twin are the ones that leave or arrive.
On a click, for every pair the script reads the word it starts from and the word it lands on: the x, the baseline (with an empty, zero-height element placed inside the word), the size, the weight, the width (font-stretch), the letter spacing in em and the colour. Both pages are always laid out, even the one hidden with visibility: hidden, so both are measured before anything moves.
In place of the real word a copy flies in a layer above the pages, with line-height 1: that way its baseline always sits at a fixed share of its size (measured once). One tween takes t from 0 to 1 and on every frame the copy works everything out again: x with power4.inOut, y with expo.inOut (two different curves bend the path) plus an upward arc, size, weight, spacing and colour with power3.inOut, and the width dropping by SQUEEZE halfway with a sine. Roboto Flex has an optical size axis, so a word that grows changes its drawing too.
Just before it arrives the copy squashes on the baseline (a scale whose origin sits right on the baseline), drops one pixel and takes on LANDING extra weight in 0.08 seconds; then elastic.out(1, 0.42) gives back shape and weight, with a one-pixel overshoot. The line under it makes the same one-pixel jump and springs back. Once it has settled the real word appears and the copy goes: they have the same size, weight and place, so the swap cannot be seen.
Each change is one timeline: the wave of leaving words (a stagger worked out from the distance to the click), the flights spaced by STAGGER in the reading order of the new page, the baselines, the new words rising once the travellers are nearly there, and the full stop last; SPEED is the timeScale. A click that comes while the page is changing waits in a queue. The two big lines fit the width and height of the screen and shrink if they would touch the rest. With reduced motion the staying words do not fly: the old page fades out and the new one fades in, already set.
| Name | File | Default | What it changes |
|---|---|---|---|
FLY | script.js | 1.1 | The seconds each word takes to fly to its new place. |
STAGGER | script.js | 0.08 | The seconds between one word setting off and the next: 0 = they all go together. |
ARC | script.js | 0.16 | How much the flight bends upward, compared with the distance: 0 = a straight line. |
SQUEEZE | script.js | 24 | How much a word narrows while it flies (on the width axis): 0 = it keeps its width. |
LANDING | script.js | 90 | The extra weight a word takes on as it lands, then gives back: 0 = no knock. |
GUIDES | script.js | true | The baselines of the new page drawing themselves under the arriving words. |
SPEED | script.js | 1 | The speed of everything: 2 is twice as fast, 0.5 half as fast. |
To move from one page to another on a quiet site where words matter: an architecture or design studio, a portfolio, a magazine, the home page leading to a section or a project, a list leading to its detail page. It works best when the two pages really share a few words: the name of a section, a project's title, a key word going from small to big.
The same transition with the sizes adapted: the first page's headline and the second page's title fit the width of the screen, the text wraps, and the words still fly between the two layouts with the same curves and landings. A tap on the link starts it.
Nothing moves: the first page fades out, the staying words appear already in place on the second, then the rest, all with fades; going back the same the other way. The entrance is a fade.
GSAP 3.15.0, Roboto Flex (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 8.8 KB; the libraries: GSAP 28 KB.
Tested on 15 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.