Skip to content
animorio
IT
Back to the library

The word written in light by pool water

WebGL, Text, HoverGSAP, WebGL, CanvasAdvancedCompressed weight: Demo 8.0 KBGSAP 28 KBFreeDownload the zip

A minimal-style WebGL animated headline: on the dark floor of a swimming pool the net of light drawn by the water, the caustics, moves and weaves; when the surface calms down the light gathers along the outline of the letters and the word reads written in light, with a blue flash. The pointer or a finger stirs the water, a click or a tap drops a pebble that opens a ring wave.

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>Lido Marne</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 written by caustics: the light on the floor of a pool, refracted by the water, focuses into the letters
       when the surface calms down; the pointer stirs the water and a tap drops a pebble -->
  <main class="pagina" data-pagina>
    <canvas class="tela" aria-hidden="true" data-tela></canvas>
    <h1 class="parola" data-parola>Lido</h1>

    <header class="testa">
      <p class="marchio" data-entra>Lido Marne</p>
      <p class="testa__nota" data-entra>Open-air pool, since 1934</p>
    </header>

    <footer class="piede">
      <p class="nota" data-entra data-nota>Move to stir the water, tap to drop a pebble</p>
      <p class="misura" data-entra>Surface <span data-calma>calm</span></p>
    </footer>
  </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.

19 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 “The word written in light by pool water” from Animorio and fit it into my site

What it does

It is night on the floor of Lido Marne, an invented open-air pool opened in 1934. The floor is almost black, with the tile joints barely visible, and above it is the water: the light passing through gathers into a net of bright lines that widen, tighten and knot, the caustics you see in every pool. Then the surface calms down, over three seconds, and the net comes apart: the light gathers along the outline of a word, Lido, raised ever so slightly under the water. The stems become two lines of light, the curves two rings, and in the corners the light tightens into small brighter knots. When the word is in focus it lights up blue for a moment, the only colour, and at the bottom the label goes from rippled to settling to calm, blue as well. Shortly after, a pebble drops: a ring wave crosses the letters, bends them and puts them back. Then the wind stirs the water again and it all starts over. The pointer, or a dragging finger, stirs the water leaving a wake of small waves; a click or a tap drops a pebble wherever you like. The tile joints sway with the surface, just as you really see them through water.

How it works

How much the water focuses the light

Everything is a single WebGL 2 fragment shader on one triangle as big as the page. For each pixel the shader asks how high the water is there and around it, at nine points two pixels apart, and works out the second derivatives. The surface acts as a lens: the light reaching the floor is 1 / |det(I + H)|, with H the matrix of second derivatives. Where the determinant goes through zero the light gathers into a bright line: that is the caustic, actually computed, not drawn.

The wind waves

The moving water is the sum of four long waves with different directions, lengths and speeds. Each wave is sin(k · x + ω · t) / k²: dividing by k² gives them all the same curvature, so none of them dominates the net. RIPPLE sets how high they are; the timeline takes them from 1 to 0.09 in CALM seconds, and that is when the net comes apart and the word remains.

The raised word

The word is written once in a 2D canvas, blurred with three box-blur passes in JavaScript and turned into a 32-bit float texture: it is the relief under the water. In 8 bits the second derivatives would be a grain of noise, so no RGBA: a single R32F channel, interpolated by hand in the shader from the four neighbours, because 32-bit floats are not filtered on their own. The blur grows with the size of the word, so the lines of light keep the same design at every size.

The pebble and the wake

Each pebble is a vec4 with position, time and strength, up to ten at once. The shader adds a ring wave running at 230 px per second, with a soft front and a fading tail. The pointer leaves a small wave every 26 px and every seven hundredths; a click or a tap leaves a strong one, PEBBLE. The tile joints shift slightly with the slope of the surface, as you see them through water.

The timeline

A looping GSAP timeline: the water calms down over CALM seconds while the relief of the word rises; a blue flash of a second and a half when it is in focus; a moment later a pebble drops on the d (skipped if someone is using the pointer); then the wind stirs the water again and the relief sinks. The shader runs on GSAP's ticker, and the label at the bottom changes only when the state changes.

Values to change

NameFileDefaultWhat it changes
WORDscript.jsLidoThe word written by the light.
FOCUSscript.js1How much the word focuses the light: higher, sharper and more doubled lines.
ROUNDscript.js14How soft the relief of the letters is, in px: higher, wider lines further from the edge.
RIPPLEscript.js1How rough the water is when it is not calm.
CALMscript.js3.2How many seconds the water takes to calm down.
PEBBLEscript.js1How strong the pebble of a click or a tap is.
TILEscript.js46The side of the floor tiles, in px.
WEIGHTscript.js860The weight of the word.

Try them in the Playground

When to use it

As the opening headline of a swimming pool, a lido, a hotel with a pool or a seaside holiday home; behind the name of a spa, a swimming school or a summer festival; as the landing page of a studio that wants a living, calm headline that only breaks up when you touch it.

On phones

The same animation: the word is sized on both width and height, and the blur of the relief grows or shrinks with it, so the lines of light look the same. A dragging finger stirs the water, a tap drops a pebble; the line at the bottom says drag instead of move.

With reduced motion

The water is already calm and still, and the word reads written in light from the first moment: no running waves, no pebbles, no timeline. The labels fade in.

Pitfalls

  • With the relief in an 8-bit texture, or even a 16-bit one, second derivatives taken two pixels apart turn into grain: the letters were outlined with dots. It needs a 32-bit float (R32F), which WebGL 2 does not filter without an extension: it is read with texelFetch and interpolated by hand.
  • The relief must curve enough: the second derivative of a step blurred with sigma s reaches about 0.24 / s² per pixel of height. With a low relief the determinant never goes through zero and no line is born: the height has to scale with the square of the blur.
  • With a wide threshold between dark and light the caustics were blobs, not lines: the light is switched on only where the focus goes above twice the normal, with a ramp up to twelve.
  • In software WebGL, or on a slow phone, frames arrive late and GSAP slows time down so as not to jump: the timeline stays right, but for tests lagSmoothing is switched off.
  • The typeface arrives after the first draw: the word relief is redrawn as soon as document.fonts.load says it is ready, and on every resize.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

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

Testing

Tested on 23 October 2026

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

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