
Tiles that rise
SVG, Hover, Physics
NewFree
A list of products with one big name per row. Over a name the word opens in two and a picture of the product appears in the gap; the lines between the names catch on the pointer, bend with it and, when it pulls too far, slip away and spring back straight, quivering.
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>Projects with elastic lines</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/dynapuff@5.3.0/index.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/dm-mono@5.3.0/500.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- the list of a small studio's products: over a name, the word parts in two and a little picture of the product
slides into the gap; the lines between the names can be caught with the pointer, bent and let go -->
<main class="pagina">
<header class="testa">
<span>Otter & Oak</span>
<span>Five things we built</span>
</header>
<ul class="elenco" data-elenco>
<li class="voce">
<a class="riga" href="#" data-riga aria-label="Tidepool, tide tables for surfers">
<span class="anteprima" aria-hidden="true">
<svg viewBox="0 0 300 150"><rect width="300" height="150" rx="22" fill="#2f7dd9"/><path d="M0 92C40 78 70 104 110 92S180 78 220 92S280 104 300 92V150H0Z" fill="#7fc4ff"/><circle cx="78" cy="58" r="18" fill="#ffe08a"/><path d="M212 40l7 15 16 2-12 11 3 16-14-8-14 8 3-16-12-11 16-2z" fill="#ff8a5c"/><text x="22" y="134" fill="#0d2a4d">tide tables for surfers</text></svg>
</span>
<span class="parola" aria-hidden="true"><span class="meta meta--sx" data-sx>TIDE</span><span class="meta meta--dx" data-dx>POOL</span></span>
</a>
</li>
<li class="voce">
<svg class="filo" data-filo aria-hidden="true" focusable="false"><path/></svg>
<a class="riga" href="#" data-riga aria-label="Lantern, a night bus planner">
<span class="anteprima" aria-hidden="true">
<svg viewBox="0 0 300 150"><rect width="300" height="150" rx="22" fill="#1d1a3a"/><rect x="120" y="34" width="60" height="78" rx="16" fill="#ffb347"/><rect x="134" y="48" width="32" height="50" rx="9" fill="#ffe7b3"/><path d="M132 34c0-16 36-16 36 0" fill="none" stroke="#ffb347" stroke-width="7"/><text x="22" y="134" fill="#c9c3ff">a night bus planner</text></svg>
</span>
<span class="parola" aria-hidden="true"><span class="meta meta--sx" data-sx>LAN</span><span class="meta meta--dx" data-dx>TERN</span></span>
</a>
</li>
<li class="voce">
<svg class="filo" data-filo aria-hidden="true" focusable="false"><path/></svg>
<a class="riga" href="#" data-riga aria-label="Pebble, a savings jar for kids">
<span class="anteprima" aria-hidden="true">
<svg viewBox="0 0 300 150"><rect width="300" height="150" rx="22" fill="#f2e2c9"/><ellipse cx="150" cy="98" rx="62" ry="22" fill="#9b8a74"/><ellipse cx="146" cy="68" rx="44" ry="17" fill="#c4b39b"/><ellipse cx="152" cy="44" rx="27" ry="12" fill="#e0d2bb"/><text x="22" y="134" fill="#5b4a35">a savings jar for kids</text></svg>
</span>
<span class="parola" aria-hidden="true"><span class="meta meta--sx" data-sx>PEB</span><span class="meta meta--dx" data-dx>BLE</span></span>
</a>
</li>
<li class="voce">
<svg class="filo" data-filo aria-hidden="true" focusable="false"><path/></svg>
<a class="riga" href="#" data-riga aria-label="Hayloft, bookings for farm stays">
<span class="anteprima" aria-hidden="true">
<svg viewBox="0 0 300 150"><rect width="300" height="150" rx="22" fill="#ffd54d"/><path d="M104 112V62l46-30 46 30v50z" fill="#d9452b"/><path d="M134 112V80h32v32" fill="#ffd54d"/><path d="M134 80l32 32M166 80l-32 32" stroke="#d9452b" stroke-width="5"/><text x="22" y="134" fill="#6b4300">bookings for farm stays</text></svg>
</span>
<span class="parola" aria-hidden="true"><span class="meta meta--sx" data-sx>HAY</span><span class="meta meta--dx" data-dx>LOFT</span></span>
</a>
</li>
<li class="voce">
<svg class="filo" data-filo aria-hidden="true" focusable="false"><path/></svg>
<a class="riga" href="#" data-riga aria-label="Mosswork, a shared garden diary">
<span class="anteprima" aria-hidden="true">
<svg viewBox="0 0 300 150"><rect width="300" height="150" rx="22" fill="#3f8f4f"/><path d="M70 104c-8-30 18-50 40-36 6-24 40-28 50-6 20-14 46 2 40 28 18 4 20 22 6 30H76c-14-2-18-12-6-16z" fill="#a8e06b"/><circle cx="118" cy="88" r="7" fill="#f6ffe0"/><circle cx="176" cy="80" r="5" fill="#f6ffe0"/><text x="22" y="134" fill="#e6ffd2">a shared garden diary</text></svg>
</span>
<span class="parola" aria-hidden="true"><span class="meta meta--sx" data-sx>MOSS</span><span class="meta meta--dx" data-dx>WORK</span></span>
</a>
</li>
</ul>
</main>
<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.
32 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 “Projects with elastic lines” from Animorio and fit it into my siteEvery name is two spans side by side. The script measures where the first half ends and where the second begins and writes into two CSS variables how far each must move so that the picture, one and a half times as wide as the letters are tall, sits in the middle with ROOM to spare on each side. With the row open, a SPLIT-second transition moves the halves on the curve 0, 0, 0.2, 1. The measurements come from the layout, which transforms do not change: they can be taken again with a row open.
Which row opens is decided by the script, not by :hover: on every mouse move it looks at the element under the pointer and opens its row. That way it also works with the preview’s autopilot, with the keyboard (a row opens when it gets the focus) and with a finger, which opens the row it touches and closes it on the second touch.
Every line is an SVG as tall as a row and centred on its edge, with a curve from the left side to the right and a single control point, in the middle while it is straight. The pointer catches it when it passes less than GRAB pixels away; from then on the control point sits under the pointer, pushed PULL times further from the middle (a curve only gets half way to its control point, so the line passes close to the pointer). The line slips away if the pointer moves more than LET_GO pixels off, or half a row if that is less, or leaves the box.
Once free, the control point goes back to the middle on a spring: on every frame, in steps of a two hundred and fortieth of a second, the force is the distance times the stiffness minus the speed times the damping, the two numbers of SPRING. The starting speed is the hand’s in the last frame: a line yanked away quickly swings more.
At the start every line has pathLength 1, so it draws itself with stroke-dashoffset alone, from 1 to 0, pushed by another spring, the two numbers of DRAW. The first starts after DRAW_DELAY[0] seconds and the others one after the other, DRAW_DELAY[1] seconds later.
| Name | File | Default | What it changes |
|---|---|---|---|
SPLIT | script.js | 0.5 | The seconds the two halves take to part (as on the site). |
ROOM | script.js | 0.18 | The room on each side of the picture, as a share of the letter size. |
GRAB | script.js | 20 | How close to the line the pointer must pass to catch it, in pixels (20 on the site). |
LET_GO | script.js | 25 | How far the line can be pulled before it slips away, in pixels (25 on the site). |
PULL | script.js | 2.2 | How many times further than the pointer the curve’s control point moves (2.2 on the site). |
SPRING | script.js | [200, 10] | The spring back: stiffness and damping. On the site they are 200 and 30, and the line comes back without swinging. |
DRAW | script.js | [300, 30] | The spring of the lines’ opening draw (the site’s). |
DRAW_DELAY | script.js | [0.15, 0.08] | The wait before the first line and between one line and the next, in seconds. |
For a studio’s list of work or a brand’s products, when the names are few and can be big: the picture only shows to whoever passes over it, and the lines give something to touch even to those who are just looking.
The same: tap a name to open it and tap again to close it, and drag a finger across a line to catch it and bend it just as with the mouse.
The halves open at once, without sliding, the picture fades in and the lines stay straight.
DynaPuff (font variabile) 5.3.0, DM Mono 5.3.0.
Compressed with gzip, the three demo files weigh 5.6 KB, and no libraries are needed.
Tested on 6 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.