
The send button
Buttons, Transitions, SVG
NewFree
A delivery app looking for a rider, shown as a grid of Truchet tiles: every tile holds two quarter circles joining its sides two by two, and a quarter turn changes which sides it joins. At rest the tiles are scrambled and no line gets from one side to the other. While it searches, the tiles near the front of the search are tried, flash and turn a quarter, and the right tile turns into place on a spring: the route lights up behind it, arc after arc, from the kitchen to the door. If no rider is near the route turns red and stops until Retry carries on; at the end the rider runs along the route, and Done scrambles the grid again.
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 route tiles</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/recursive@5.3.0/full.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the route tiles: a food delivery app looking for a rider, and the search is a grid of Truchet tiles, each with two
quarter arcs. Scrambled at rest; while it searches the tiles turn a quarter at a time until one path runs
unbroken from the kitchen to the door, and the route lights up behind them. The tiles are built by script.js -->
<section class="rt" aria-labelledby="rt-title" data-route>
<div class="rt__head">
<div class="rt__words">
<h2 class="rt__title" id="rt-title"><span class="rt__swap" data-title>Your order</span></h2>
<p class="rt__meta"><span class="rt__swap" data-meta>Osteria Lume · 2 items · €31.40</span></p>
</div>
<p class="rt__figure" aria-hidden="true">
<span class="rt__pct" data-pct>0%</span>
<svg class="rt__tick" viewBox="0 0 28 28" data-tick><path d="M6.5 14.8l5 5 10-11.4" pathLength="1"/></svg>
</p>
</div>
<div class="rt__map" role="progressbar" aria-labelledby="rt-title" aria-valuemin="0" aria-valuemax="100"
aria-valuenow="0" aria-valuetext="Not started" data-bar>
<svg class="rt__svg" viewBox="0 0 408 208" aria-hidden="true" data-svg>
<g data-tiles></g>
<path class="rt__route" data-route-line/>
<path class="rt__rider" data-rider/>
<g class="rt__cap rt__cap--from" data-from>
<rect x="-6" y="-6" width="12" height="12" rx="3"/>
</g>
<g class="rt__cap rt__cap--to" data-to>
<path d="M-6.5 6.5V-1L0 -7l6.5 6V6.5z"/>
</g>
</svg>
</div>
<div class="rt__ends" aria-hidden="true">
<span>Osteria Lume</span>
<span>14 Via Sole</span>
</div>
<div class="rt__foot">
<span class="rt__status"><span class="rt__swap" data-status>Paid · ready to send</span></span>
<button class="pill" type="button" data-btn data-autoclic>
<span class="pill__fill" aria-hidden="true"></span>
<span class="pill__label" data-label>Find a rider</span>
</button>
</div>
<p class="sr" role="status" 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.
31 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 route tiles” from Animorio and fit it into my siteEvery tile is an SVG group with two quarter circles of half its side: turned 0 degrees it joins the top side to the left and the bottom to the right; turned a quarter it joins the top to the right and the bottom to the left. A half turn changes nothing, so its way round is the number of quarter turns modulo two, and GSAP turns the group round the middle of the tile with svgOrigin.
Entering a tile by one side, the route can only leave by one of the two sides next to it: it has to turn at every tile. The route is a walk that never goes through the same tile twice, found depth first with a seeded generator: it starts from the left in a random row, prefers the ways out to the right with a little chance, backs up when it is shut in, and only counts if it leaves by the right side after at least one and a half times the columns. Every tile of the route notes the way round it needs.
At rest every tile of the route is turned the wrong way and the others at random, from the seed, so the grid lets nothing through. While searching, at every step, up to TRIES tiles near the front (at most two tiles away) light up and turn a quarter with a light back.out; then the route’s tile turns a quarter (or half, if it were already right) with a back.out of SPRING, going past and back.
The lit route is a single SVG path, built once: a stretch from the kitchen, an A arc per tile (centred on the corner between the side it enters and the one it leaves, its direction from the cross product) and a stretch to the door. Every arc is π·r/2 long, so the lit length is the sum of the arcs of the tiles already in place, and stroke-dashoffset follows it on a tween. The rider is the same path with a single short dash, run along by stroke-dashoffset.
Going back to rest every tile turns one, two or three quarters back to its starting way round, in a wave that starts at the door: since a half turn does not change a tile, the maze comes back exactly as it was even though the angles differ, and the preview’s round starts over the same.
| Name | File | Default | What it changes |
|---|---|---|---|
GRID | script.js | [9, 5] | Columns and rows of tiles. |
SECONDS | script.js | 6 | How many seconds the search takes in the demo. |
TRIES | script.js | 2 | How many tiles near the front are tried before each tile of the route. |
SPRING | script.js | 2.4 | How far a tile turning into place overshoots. |
SEED | script.js | 5 | The route and the scramble: another number, another route. |
MINUTES | script.js | 18 | How many minutes the rider will take, said at the end. |
FAIL_EVERY | script.js | 2 | One search in how many finds no rider at first (0 never). |
FAIL | script.js | 0.58 | Where the search stalls, from 0 to 1. |
PREVIEW | script.js | 1.4 | In the preview’s round: how many times faster. |
LABELS | script.js | ['Find a rider', 'Cancel', 'Retry', 'Done'] | The words of the pill, state by state. |
--route | style.css | #ffd23f | The colour of the route. |
--tile | style.css | #34332f | The colour of the tiles’ arcs. |
For finding a rider in a delivery or restaurant app, for working out a route in a shipping, taxi or maps app, for confirming an order with delivery, for any wait where something has to find its way from one point to another.
The panel takes the whole width less the margins and the grid, an SVG with its own viewBox, shrinks with it: the tiles are the same, a little smaller. The pill answers a touch as it does the mouse.
The tiles do not turn: they fade out and back in already turned. The route still lights up tile after tile, because it is the progress, but without sliding; the rider does not run, the stalled tile does not shake and the words change without moving.
GSAP 3.15.0, Recursive (font variabile, assi MONO, CASL e wght) 5.3.0.
Compressed with gzip, the three demo files weigh 9.1 KB; the libraries: GSAP 28 KB.
Tested on 19 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.