
The layered button
Buttons, Hover, Physics
Free
The footer of a small travel agency, all of it sky: along the top five little planes in formation write the agency's name in puffs of smoke, one row of dots each, the way skytyping is really done. The puffs swell and merge into letters of cloud, shaded on the side away from the sun, and the wind slowly carries them; the pointer blows the smoke away and curls it, a click is a gust, and once the writing is undone the formation comes back to write it again. A tiny plane flies over each link and lifts its letters, and signing up has the thank you written in the sky.
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 skywritten footer</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the footer of a small travel agency, all sky: along its top a formation of five little planes flies across
and skytypes the agency's name in puffs of smoke, one row of the letters for each plane. The smoke billows,
drifts on the wind and can be blown about with the pointer; a click is a gust. A link pointed at has a tiny
plane fly over it, its letters lifted in its wake, and signing up to the fare letter calls the formation
back to write thank you -->
<footer class="fw" data-footer data-autoclic>
<canvas class="fw__sky" aria-hidden="true" data-sky></canvas>
<div class="fw__top">
<form class="fw__form" action="#" novalidate aria-labelledby="fw-news" data-form>
<h2 class="fw__title" id="fw-news" data-in>The Friday fare drop</h2>
<p class="fw__lede" data-in>Empty seats, quiet seasons and the small-group trips with places left.</p>
<label class="sr" for="fw-email">Email address</label>
<div class="fw__field" data-field data-in>
<input class="fw__input" id="fw-email" name="email" type="email" inputmode="email" autocomplete="email" autocapitalize="off" spellcheck="false" enterkeyhint="send" placeholder="you@example.com" aria-describedby="fw-note" aria-invalid="false" data-input>
<button class="fw__send" type="submit" data-send>
<span class="fw__send-bg"></span>
<span class="fw__send-label" data-send-label>Subscribe</span>
<svg class="fw__spin" viewBox="0 0 24 24" aria-hidden="true" data-spin><circle cx="12" cy="12" r="8"/></svg>
<svg class="fw__done" viewBox="0 0 24 24" aria-hidden="true" data-done><path d="M6.4 12.6l3.7 3.7 7.6-8.6"/></svg>
</button>
</div>
<p class="fw__note" id="fw-note" aria-live="polite" data-note data-in><span class="fw__line">Fridays only. Leave with one click.</span></p>
</form>
<nav class="fw__cols" aria-label="Footer">
<div class="fw__col" data-in>
<h3 class="fw__head">Destinations</h3>
<ul>
<li><a class="fw__link" href="#" data-link>Lisbon</a></li>
<li><a class="fw__link" href="#" data-link>Kyoto</a></li>
<li><a class="fw__link" href="#" data-link>Oaxaca</a></li>
<li><a class="fw__link" href="#" data-link>Lofoten</a></li>
</ul>
</div>
<div class="fw__col" data-in>
<h3 class="fw__head">Trips</h3>
<ul>
<li><a class="fw__link" href="#" data-link>Small groups</a></li>
<li><a class="fw__link" href="#" data-link>By rail</a></li>
<li><a class="fw__link" href="#" data-link>Gift a trip</a></li>
<li><a class="fw__link" href="#" data-link>Travel journal</a></li>
</ul>
</div>
<div class="fw__col" data-in>
<h3 class="fw__head">Agency</h3>
<ul>
<li><a class="fw__link" href="#" data-link>Our guides</a></li>
<li><a class="fw__link" href="#" data-link>Visas and insurance</a></li>
<li><a class="fw__link" href="#" data-link>Careers</a></li>
<li><a class="fw__link" href="#" data-link>Contact</a></li>
</ul>
</div>
</nav>
</div>
<div class="fw__base" data-in>
<p class="fw__brand">
<svg class="fw__mark" viewBox="0 0 30 14" aria-hidden="true"><path d="M29 7.2c0-2.2-1.6-3-4-3l-4.6.1L6 5.8 2.8 1.2C2.4.6 1.2.6 1 1.6l.8 6.7c.1.6.5.9 1.1 1l4.2.6-2.4 3.1h2.4l4.6-2.6 7.6.6c2.6 0 4-.6 6.2-1.4C27.8 9.1 29 8.6 29 7.2z"/></svg>
<span class="fw__name">Faraway Travel</span>
<span class="fw__copy"><svg viewBox="0 0 12 12" aria-hidden="true"><circle cx="6" cy="6" r="5.1"/><path d="M7.9 4.5a2.3 2.3 0 1 0 0 3"/></svg><span class="sr">Copyright</span> 2026 Faraway Travel Ltd</span>
<span class="fw__hours">Desk open Monday to Saturday, 9:00 to 18:30</span>
</p>
<nav class="fw__legal" aria-label="Legal">
<a class="fw__link fw__link--small" href="#" data-link>Privacy</a>
<a class="fw__link fw__link--small" href="#" data-link>Terms</a>
<a class="fw__link fw__link--small" href="#" data-link>Cookies</a>
</nav>
</div>
</footer>
</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.
55 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 skywritten footer” from Animorio and fit it into my siteEvery letter is a grid five dots high and three to five wide, written as lines of text ('.##.', '#..#'…). The word turns into five lists of columns, one per row, with an empty column between letters. The size of a dot comes from the longer of NAME and THANKS, so both writings have the same pitch and always fit in the sky, phone included.
A GSAP tween with no easing carries the formation from one side to the other at SPEED pixels a second; each plane flies its own row, a little further back the further out it is, in an arrowhead. On every update, for every row, as long as the plane's tail has passed the column of the next dot, PUFFS puffs are born there, with a small random offset and already aged by the time the tail has gone past: so the letters stay straight even if a frame skips.
Each puff is a point with a speed, an age and a size: its radius grows as 1 - e^(-age/0.42) and merges with its neighbours. They are drawn with two soft images made once and stamped with drawImage: first all the shadows, moved down and to the left, then all the lights, so the puffs become one cloud, bright towards the sun. The speed eases towards the wind plus a turbulence of crossing waves, the same for nearby puffs, so the writing warps slowly instead of falling apart.
The pointer draws the nearby puffs towards a share of its speed, never beyond, with a bell-shaped weight, plus a sideways push that turns around its path and curls the smoke; a click pushes the ones around it outwards. A soft floor sends back up the smoke that sinks towards the links. How much the writing has been stirred adds up: past a threshold the wind takes it sooner, so the formation comes back to write it again.
After LINGER seconds a GSAP call lets the writing go: over FADE seconds the puffs spread by more than a dot, the turbulence and the wind grow, the smoke rises a little and the opacity falls on a soft curve. When the sky is empty a new pass starts; the sign-up calls one at once with THANKS. The drawing loop only runs while there is smoke or a plane, stops off screen, and its step is at most 1/30 of a second.
| Name | File | Default | What it changes |
|---|---|---|---|
NAME | script.js | 'FARAWAY' | The word the planes write in the sky: letters A to Z and spaces, up to eight letters so it stays big. |
THANKS | script.js | 'THANKS' | The word they come back to write when someone signs up. |
CELL | script.js | 19 | How far apart the dots of a letter are on a computer, in pixels: less on a phone, so the longest word fits. |
SPEED | script.js | 380 | How fast the formation flies, in pixels a second. |
WIND | script.js | 8 | How fast the wind carries the smoke to the right, in pixels a second (below zero to the left). |
LINGER | script.js | 9 | How many seconds the writing hangs in the sky before the wind takes it and the planes write it again. |
STIR | script.js | 1 | How much the pointer stirs the smoke as it passes through: at 0 it leaves it alone. |
PUFFS | script.js | 3 | How many puffs make each dot of a letter: more makes fuller, cloudier letters. |
WAIT | script.js | 1.1 | How long the sign-up takes in the demo, in seconds. |
For the footer of a travel agency, a small airline, a flying club, a flight or skydiving school, an open-air festival or a summer event: anywhere the bottom of the page can be a sky, and the brand's name deserves to be written up there.
The footer takes the width of the screen and the band of sky gets lower: the word gets smaller (the dots go down to about 10 pixels) so the longest one fits, and the planes get shorter with it. The newsletter goes on top, then the columns stack, two side by side and the third underneath, with taller rows to tap; the mark, the copyright, the hours and the legal links go one under the other. A finger dragged sideways through the sky stirs the smoke like the pointer (up and down scrolls the page), a tap is a gust, and tapping a link sends the tiny plane over it as with the hover.
No planes and no wind: the writing appears already made, still and fading in, in the middle of the sky, and the pointer does not stir it. On sign-up FARAWAY fades out and THANKS fades in in its place, and after a few seconds it turns back into FARAWAY the same way. Pointing at a link the word only changes colour and the tiny plane does not fly; the button does not narrow, the arc keeps still and a wrong field does not shake.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 16 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.