
Words that melt into each other
Text, Scroll, SVG
NewFree
Scroll inside the preview
One thin line crosses the screen; as you scroll it bends and ties itself in a wave that runs from left to right, until it writes “hello” in joined-up handwriting, in a single stroke. Then the knots come undone, the smallest first, the line straightens, shortens and becomes the line under the title, which rises out of it; as it lands it twangs like a plucked string. Minimal style.
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 line that ties itself</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- a made-up identity studio, Linework. The stage sticks while you scroll: one thin line crosses the screen, bends
and ties itself into the word "hello" in joined-up handwriting, then lets go of its loops, straightens out and
becomes the line under the title. The line is drawn by the script -->
<main>
<section class="corsa" data-corsa>
<div class="palco" data-palco>
<svg class="tela" data-tela aria-hidden="true">
<path class="filo" data-filo d="M0 0"/>
</svg>
<header class="testa">
<a class="marchio" href="#" data-marchio data-entra-testa>
<svg class="marchio__segno" viewBox="0 0 34 20" aria-hidden="true">
<path data-marchio-filo d="M1 15h7c4 0 6-3 7-7s-1-6-3-5-2 5 1 9 7 3 9 3h11"/>
</svg>
<span class="marchio__nome">Linework</span>
</a>
<a class="pillola" href="#" data-pillola data-entra-testa><span class="pillola__fondo" data-pillola-fondo></span><span class="pillola__testo">Say hello</span></a>
</header>
<p class="nota" data-entra-testa><span data-nota>Brand identities, drawn in a single stroke</span></p>
<h1 class="titolo" data-titolo aria-label="Hello.">Hello.</h1>
<span class="titolo titolo--misura" data-misura aria-hidden="true">Hello.</span>
<p class="sotto" data-sotto>Every identity we make starts as one line.</p>
<p class="cenno" data-entra-testa><span class="cenno__dentro" data-cenno><span>Scroll</span><svg viewBox="0 0 20 20" aria-hidden="true"><path d="M10 3v13M4.5 10.5 10 16l5.5-5.5"/></svg></span></p>
</div>
</section>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lenis@1.3.26/dist/lenis.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.
20 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 line that ties itself” from Animorio and fit it into my siteThe word hello is a list of points in letter heights (the baseline at 0, the small letters 1 high, the loops 2.45), slanted with x minus y times SLANT. Before and after it there are two straight strokes out to the edges of the screen, and a centripetal Catmull-Rom spline runs through all the points: the centripetal kind makes no little curls and spikes where the points are close, as in the loops. The path is resampled every 2 pixels, so every step is the same length.
For every step the script knows its direction, unwrapped (without the 2π jumps), so how much the line turns between two steps is a plain difference. The line is rebuilt every frame from the left edge by adding up those turns: behind the wave it takes all of them and the word is exact, inside the wave only part of them (a smoothstep plus a small extra, so the loop closes a touch too far and settles back), ahead of the wave none. The rest of the line is a Bézier curve from the point where the wave ends, in its direction, to the right edge: the pulled thread.
Nothing is rotated to straighten it: the word becomes a straight chord between its first and last point plus a displacement, written as a sum of sines (a discrete sine transform). The heat flow dims every sine by exp(−λ·τ), and the dense sines have a large λ: that is why the small curls melt first, then the letters, then the long wave. τ grows exponentially with the scroll, so every size of detail gets the same stretch of page. Meanwhile the ends of the chord go to the ends of the title and the two side strokes pull back.
The title has an invisible copy holding a zero-height element on the baseline: from it the script reads where the baseline falls and how wide the title is, and puts the line a tenth of the font size below it. The title is cut with clip-path just above the line, so the letters, rising from yPercent 118 with back.out, seem to come out of the line; meanwhile font-variation-settings takes them from wght 360 and wdth 75 to 620 and 100. The line gets INK times thicker as it shortens.
Everything is one timeline with ScrollTrigger and scrub, on a sticky stage 100lvh tall with Lenis for smooth scrolling: first the writing wave, a pause, the letting go, the title and the sentence. The line is redrawn in GSAP’s ticker only when something has changed. When the letting go reaches the end, scrolling down, a timed tween with elastic.out fires once and makes the line quiver like a string (the offset is one sine as long as the line) and the letters hop from the middle.
| Name | File | Default | What it changes |
|---|---|---|---|
TITLE | script.js | 'Hello.' | The title that rises out of the line at the end; the line becomes as wide as it. |
WORD_SIZE | script.js | 0.62 | How wide the handwritten word is, as a share of the screen (a little more on a phone); it never gets taller than a third of the screen. |
LINE | script.js | 1.6 | The thickness of the line, in pixels. |
SLANT | script.js | 0.22 | How far the handwriting leans to the right (0 = upright). |
BEND | script.js | 1.4 | How long the stretch of line that is bending is, in letter heights: more is softer, less is a sharp knot. |
SPRING | script.js | 1 | How far every loop closes too far before settling back (0 = not at all). |
INK | script.js | 2.2 | How much thicker the line gets as it shortens into the title’s underline (1 = the same). |
TWANG | script.js | 1 | How much the underline quivers when it lands and under the mouse (0 = still). |
RIDE | script.js | 1 | How much scrolling the whole thing takes: 2 is twice as much. |
--filo | style.css | #ff4b1f | The colour of the line. |
For the opening of a studio, a designer or a brand that wants to say “everything starts with a gesture”: a handwritten greeting that turns into the set title. It also works at the top of a contact or about page, or to go from a story to a heading.
The same: the word is almost as wide as the screen, the line is resampled more finely, the title is a quarter of the screen wide and rises the same way. A tap on the title plucks the line.
Nothing bends or quivers and Lenis stays off: the scroll goes through three stops, the straight line, the written word and the title with its underline, and between one stop and the next the drawing changes with a fade.
GSAP 3.15.0, ScrollTrigger 3.15.0, Lenis 1.3.26, Instrument Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 9.8 KB; the libraries: GSAP + ScrollTrigger 45 KB, Lenis 5.3 KB.
Tested on 14 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.