Skip to content
animorio
IT
Back to the library

Colour that clings

WebGL, HoverWebGL, CanvasAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

A picture printed in grey. The pointer stirs a fluid that brings the colours back where it passes; the colour stays much longer on near things, which also take it sooner, and the picture’s three layers shift a little with the pointer.

The code

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>Colour that clings</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/index.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/barlow@5.3.0/400.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/barlow@5.3.0/600.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <header class="bar">
    <a class="brand" href="#">Highfield Balloon Week</a>
    <nav class="bar__nav" aria-label="Sections">
      <a href="#">Programme</a>
      <a href="#">Flights</a>
      <a href="#">Visit</a>
    </nav>
  </header>

  <!-- the picture: three layers drawn by the script (sky, hills, the big balloon), grey until the pointer stirs colour in -->
  <main class="stage" data-stage>
    <canvas class="stage__canvas" data-canvas aria-hidden="true"></canvas>
    <div class="stage__copy">
      <p class="kicker">14–17 September · Highfield Downs</p>
      <h1 class="title">Up before <em>the sun.</em></h1>
      <p class="note" data-hint>Run the pointer over the picture: the colour follows it, and stays longest on what is closest.</p>
    </div>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

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.

26 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “Colour that clings” from Animorio and fit it into my site

What it does

The opening of an invented balloon festival, Highfield Balloon Week: bottom left «Up before the sun.», everything else is a picture drawn by the script and printed in grey. A dawn sky with eight far balloons, hills with trees, a red barn and three balloons halfway, and on the right a big balloon in coloured gores with its basket, with tall grass in front. Passing the pointer stirs a fluid, and where its dye reaches the picture is in colour again, with a hard edge. On the sky the colour goes in a second and a half; on the big balloon, which is near, it stays for several seconds, and the balloon takes its colour with less fluid too. Meanwhile the picture’s three layers shift slightly with the pointer, the near ones more than the far ones. On phones you paint with a finger.

How it works

The picture in three layers

The script draws the picture on three canvases, the sky with the far balloons, the hills with the halfway ones, the big balloon with the grass, and once more all together in greys for the depth: white near, black far. The four canvases become WebGL textures; at a new screen size they are drawn again.

The fluid

It is a fluid in WebGL2 on a grid of GRID cells per pixel, at half precision. The brush pours velocity (FORCE for every pixel of movement) and dye (DYE per touch), in several touches along fast strokes; then vorticity (CURL), divergence, ITERATIONS steps of pressure, the gradient and the advection. Velocity and dye keep VELOCITY_FADE and TRAIL_FADE every sixtieth of a second.

The depth in the fluid

Depth changes the fluid in three places. On near things the dye fades much less: its factor moves towards 0.998 in proportion to depth times CLING. Near things take colour sooner: the threshold is THRESHOLD times (1 minus BIAS times the depth). And they push the fluid aside: BLOCK times the depth is added to the pressure.

The screen

The shader reads the three layers shifted in proportion to their depth (PARALLAX times the pointer’s position, followed with a lag), stacks them, and shows them in grey, the luminance times GREY, or in colour where the fluid’s dye passes the threshold, with an edge (1 minus EDGE) times 0.35 wide. A slight zoom keeps the edges hidden when the layers shift.

When it stops

After the last move the fluid runs until the colour on the nearest things is gone, about 14 seconds with these values; then the fields are cleared and the frames stop until the pointer comes back.

Values to change

NameFileDefaultWhat it changes
BRUSHscript.js2.8The size of the brush, in thousandths of the picture’s height squared.
FORCEscript.js3How hard the pointer pushes the fluid, for every pixel it moves.
DYEscript.js0.65How much colour every touch of the brush pours in.
CURLscript.js4How much the fluid curls (0 = no swirls).
VELOCITY_FADEscript.js0.906How much speed is left after every sixtieth of a second.
TRAIL_FADEscript.js0.947How much colour is left after every sixtieth of a second on the far things.
CLINGscript.js0.9How hard the colour holds on to near things: 0 = as on far ones, 1 = it hardly fades.
BLOCKscript.js0.33How much the near things push the fluid aside.
BIASscript.js0.9How much sooner the near things take their colour.
THRESHOLDscript.js0.66How much dye a far thing needs before its colour shows.
EDGEscript.js0.97How hard the edge of the colour is (0 = soft, 1 = hard).
GREYscript.js0.92The brightness of the grey print.
PARALLAXscript.js0.015How far the layers shift with the pointer.
ITERATIONSscript.js12The steps of the pressure: more steps, a more precise fluid.
GRIDscript.js0.4The cells of the fluid for every pixel of the screen.

Try them in the Playground

When to use it

For a picture that should invite a touch: a place, a product, a person at the opening. It works best with a scene that has layers: the near thing keeps its colour, the background loses it quickly.

On phones

Without a pointer you paint with a finger: on the picture the finger does not scroll the page. The layers shift following the finger, the brush is the same.

With reduced motion

No fluid and no shifting layers: the picture is there at once, still and in colour.

Pitfalls

  • The fluid’s textures are half precision: before starting, the script checks that an RGBA16F target can be written to, otherwise the picture is shown in colour without the fluid.
  • A canvas that has already tried WebGL cannot draw in 2D any more: the fallback needs a fresh canvas in its place.
  • The depth under a point must be read with the same shift as its layer, so the edge of the colour stays on the thing when the layers move.
  • Without the depth the fluid colours everything the same way: it is the colour lasting longer on near things, and reaching them sooner, that makes it look as if it clings.

Libraries

GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Barlow 5.3.0.

Compressed with gzip, the three demo files weigh 12 KB; the libraries: GSAP 28 KB.

Testing

Tested on 2 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: Done

License

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.

All animations