
Ink that reveals
WebGL, Hover
Free
A minimal-style WebGL animation: a large word made of a few thousand specks of ink on a warm white. They fly in from a loose cloud and settle on the word; the pointer blows them aside and they spring back to their place, and the ones carried furthest flash vermilion for a moment.
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>Flock</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>
<!-- a word made of a few thousand specks of ink, drawn by WebGL in one draw call: they fly in from a loose cloud
and settle on the word; the pointer blows them aside like a breath, and they spring back to their place -->
<main class="pagina" data-pagina>
<canvas class="tela" aria-hidden="true" data-tela></canvas>
<header class="testa">
<p class="marchio" data-entra>Flock</p>
<p class="testa__nota" data-entra>A motion studio</p>
</header>
<h1 class="parola" data-parola>Flock</h1>
<p class="nota" data-entra data-nota>Move through the word, click to scatter</p>
</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.
17 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 word made of particles” from Animorio and fit it into my siteThe word is written in a canvas as big as the page, never shown, at the size that fits it into 80% of the width (88% on a phone) and 42% of the height. getImageData gives the filled pixels: counting them gives the step of a grid that yields about COUNT specks on any screen, and every grid point, nudged a little at random, becomes a speck if it falls on a filled pixel. The randomness has a fixed seed, so the grain is always the same.
Every speck has its home, its position and its velocity in Float32Arrays. On every frame, in substeps of 1/120 of a second, a spring pulls it home (SPRING) and damps it (DAMPING); the pointer pushes it away with a force that falls off with the square of the distance inside a radius that widens with the pointer’s speed. Before they set off, the specks in the cloud only float a little.
Every speck has a delay between 0 and 1, two thirds from its horizontal position and one third from chance; a single number, animated by GSAP from 0 to 2 over twice ARRIVAL, says who has already left. The breath only switches on once that number passes 1.4, that is once the word is complete.
All the specks are one buffer of three numbers each (x, y and how hot it is) drawn with gl.POINTS: the vertex shader goes from pixels to screen coordinates and the fragment shader cuts a round dot and mixes ink and accent by the heat. Heat rises with the distance from home and falls off exponentially. Without WebGL the same specks are drawn on a 2D canvas in two passes.
A GSAP timeline with repeat -1 takes a number from 0 to 1 in ten seconds: it is the position on a figure of eight drawn over the word, which acts as the pointer when the real one has been still for more than 2.4 seconds. A click, a tap, Enter or space give every speck a push in a random direction. On resize the word is resampled and the specks keep their old position, so they spring back home. With reduced motion the word is already built and still.
| Name | File | Default | What it changes |
|---|---|---|---|
WORD | script.js | 'Flock' | The word made of specks: short, it fits on one line. |
COUNT | script.js | 6000 | Roughly how many specks (a little over half on a phone). |
DOT | script.js | 2 | The diameter of a speck, in px. |
WEIGHT | script.js | 820 | The weight of the word: Archivo goes from 100 to 900, heavier means more specks in the stems. |
PUSH | script.js | 1 | How hard the pointer blows (0 it does not). |
REACH | script.js | 110 | How far the breath reaches, in px: it widens when the pointer runs. |
SPRING | script.js | 60 | How stiff the spring that takes the specks home is. |
DAMPING | script.js | 9 | How damped the spring is: lower means more overshoot and wobble. |
ARRIVAL | script.js | 2.4 | How many seconds the cloud takes to settle on the word. |
As the opening of a motion studio, a creative agency or a product about data, networks or crowds; as the title of a conference or a festival; anywhere a single word should feel alive and react to the hand.
The same with a finger: dragging blows, tapping scatters. The word fits 88% of the width, there are a little over half the specks and the breath is a little narrower.
The word is already built and still, it fades in; the pointer and the click move nothing.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 7.3 KB; the libraries: GSAP 28 KB.
Tested on 21 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.