
The colour columns
Hover, SVG
NewFree
The header of a made-up river nature reserve, Holt, in the rich style and full of detail: a river at sunset seen from the side, above and below the water. A big otter with wet fur, whiskers and webbed feet floats on its back tapping a stone on a shell, rubs its face, dives, swims under the water through the eel grass and comes up with a fish that it eats on its back, then rolls over. Around it a willow, a boardwalk, a fallen log with a kingfisher that dives, a heron, fish, a crayfish, lily pads, reeds and dragonflies. A pointer or a finger near the water makes rings and the otter swims over to have a look.
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 otter in the river</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- a made-up river reserve, Holt. A river seen from the side, above and below the water: hills in the haze, alders,
birches and a heron on the far bank, a willow hanging over the water, a boardwalk, a fallen log with a kingfisher
on its branch, banks with roots, stones and ferns, lily pads, eel grass, fish, a crayfish under a stone, light
moving on the riverbed. A big otter floats on its back tapping a stone on a shell, grooms its face, rolls over,
dives, swims under the water and comes up with a fish to eat. A pointer or a finger near the water makes rings,
and the otter swims over to have a look. All drawn by the script -->
<main class="riserva" data-riserva>
<svg class="scena" data-scena viewBox="0 0 1600 900" preserveAspectRatio="xMidYMax slice" role="img" aria-labelledby="scena-nome">
<title id="scena-nome">A river otter floating on its back by a boardwalk, grooming, diving and coming up with a fish, among lily pads, reeds, a willow and a kingfisher</title>
<defs>
<linearGradient id="g-cielo" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--cielo-alto)"/>
<stop offset=".7" stop-color="var(--cielo)"/>
<stop offset="1" stop-color="var(--cielo-basso)"/>
</linearGradient>
<radialGradient id="g-sole" cx=".5" cy=".5" r=".5">
<stop offset="0" stop-color="var(--sole)" stop-opacity=".95"/>
<stop offset=".3" stop-color="var(--sole)" stop-opacity=".5"/>
<stop offset="1" stop-color="var(--sole)" stop-opacity="0"/>
</radialGradient>
<linearGradient id="g-nuvola" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#fffaf0"/>
<stop offset=".6" stop-color="#fbe9cb"/>
<stop offset="1" stop-color="#eec99a"/>
</linearGradient>
<linearGradient id="g-colline-1" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--collina-lontana)"/>
<stop offset="1" stop-color="var(--cielo-basso)"/>
</linearGradient>
<linearGradient id="g-colline-2" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--collina-chiara)"/>
<stop offset="1" stop-color="#b9c79a"/>
</linearGradient>
<linearGradient id="g-colline-3" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--collina)"/>
<stop offset="1" stop-color="#83a873"/>
</linearGradient>
<linearGradient id="g-foschia" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--cielo-basso)" stop-opacity="0"/>
<stop offset="1" stop-color="var(--cielo-basso)" stop-opacity=".55"/>
</linearGradient>
<linearGradient id="g-acqua" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--acqua-chiara)"/>
<stop offset=".12" stop-color="var(--acqua)"/>
<stop offset=".5" stop-color="var(--acqua-media)"/>
<stop offset="1" stop-color="var(--acqua-fonda)"/>
</linearGradient>
<linearGradient id="g-velo" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--acqua)" stop-opacity=".3"/>
<stop offset=".35" stop-color="var(--acqua-media)" stop-opacity=".34"/>
<stop offset="1" stop-color="var(--acqua-fonda)" stop-opacity=".58"/>
</linearGradient>
<linearGradient id="g-raggio" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#fff6d6" stop-opacity=".55"/>
<stop offset=".6" stop-color="#e8fff0" stop-opacity=".12"/>
<stop offset="1" stop-color="#e8fff0" stop-opacity="0"/>
</linearGradient>
<linearGradient id="g-sabbia" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--sabbia)"/>
<stop offset="1" stop-color="var(--sabbia-scura)"/>
</linearGradient>
<linearGradient id="g-riva" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--terra)"/>
<stop offset=".45" stop-color="var(--terra-media)"/>
<stop offset="1" stop-color="var(--terra-scura)"/>
</linearGradient>
<linearGradient id="g-pelo-galla" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--pelo-chiaro)"/>
<stop offset=".28" stop-color="#b07c4e"/>
<stop offset=".6" stop-color="var(--pelo)"/>
<stop offset="1" stop-color="var(--pelo-scuro)"/>
</linearGradient>
<linearGradient id="g-pelo" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--pelo-scuro)"/>
<stop offset=".55" stop-color="var(--pelo)"/>
<stop offset="1" stop-color="#a87a4f"/>
</linearGradient>
<linearGradient id="g-testa" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--pelo-scuro)"/>
<stop offset=".7" stop-color="var(--pelo)"/>
<stop offset="1" stop-color="#94643d"/>
</linearGradient>
<linearGradient id="g-muso" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#d9b48a"/>
<stop offset="1" stop-color="var(--pelo-chiaro)"/>
</linearGradient>
<radialGradient id="g-sasso" cx=".35" cy=".3" r=".8">
<stop offset="0" stop-color="#d3ccbd"/>
<stop offset=".55" stop-color="#9a9384"/>
<stop offset="1" stop-color="#5a554c"/>
</radialGradient>
<radialGradient id="g-sasso-scuro" cx=".35" cy=".3" r=".8">
<stop offset="0" stop-color="#a39a86"/>
<stop offset=".6" stop-color="#6c665a"/>
<stop offset="1" stop-color="#3d3a33"/>
</radialGradient>
<radialGradient id="g-sasso-caldo" cx=".35" cy=".3" r=".8">
<stop offset="0" stop-color="#d8bf98"/>
<stop offset=".6" stop-color="#9c7f5a"/>
<stop offset="1" stop-color="#5e4a33"/>
</radialGradient>
<radialGradient id="g-foglia" cx=".4" cy=".35" r=".75">
<stop offset="0" stop-color="var(--ninfea-chiara)"/>
<stop offset="1" stop-color="var(--ninfea)"/>
</radialGradient>
<linearGradient id="g-ala" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#e9fbff" stop-opacity=".7"/>
<stop offset="1" stop-color="#e9fbff" stop-opacity=".25"/>
</linearGradient>
<linearGradient id="g-pesce" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#5f6f43"/>
<stop offset=".45" stop-color="#a9b382"/>
<stop offset=".7" stop-color="#dfe3cf"/>
<stop offset="1" stop-color="#f4f1e2"/>
</linearGradient>
<linearGradient id="g-pesce-2" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#6b5a33"/>
<stop offset=".45" stop-color="#c2a463"/>
<stop offset=".75" stop-color="#ead9ad"/>
<stop offset="1" stop-color="#f6eedb"/>
</linearGradient>
<pattern id="p-squame" width="7" height="6" patternUnits="userSpaceOnUse">
<path d="M0 3A3.5 3.5 0 0 0 7 3M-3.5 6A3.5 3.5 0 0 0 3.5 6M3.5 6A3.5 3.5 0 0 0 10.5 6M-3.5 0A3.5 3.5 0 0 0 3.5 0M3.5 0A3.5 3.5 0 0 0 10.5 0" fill="none" stroke="#2c3220" stroke-width=".7" opacity=".5"/>
</pattern>
<linearGradient id="g-martin-dorso" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#3fc0d6"/>
<stop offset=".5" stop-color="#1886a8"/>
<stop offset="1" stop-color="#0f5f7e"/>
</linearGradient>
<linearGradient id="g-martin-ventre" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#f3a04a"/>
<stop offset="1" stop-color="#d9702a"/>
</linearGradient>
<linearGradient id="g-tronco" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#9b7a55"/>
<stop offset=".5" stop-color="#6e5338"/>
<stop offset="1" stop-color="#3e2d1d"/>
</linearGradient>
<linearGradient id="g-betulla" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#e9e2d4"/>
<stop offset=".6" stop-color="#fbf7ee"/>
<stop offset="1" stop-color="#cfc6b4"/>
</linearGradient>
<linearGradient id="g-salice" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#7fa54e"/>
<stop offset="1" stop-color="#a9c867"/>
</linearGradient>
<linearGradient id="g-gambero" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#8a4a2a"/>
<stop offset="1" stop-color="#c47a45"/>
</linearGradient>
<linearGradient id="g-riverbero" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--sole)" stop-opacity=".6"/>
<stop offset="1" stop-color="var(--sole)" stop-opacity="0"/>
</linearGradient>
<clipPath id="c-sotto"><path data-clip-sotto d="M0 470H1600V900H0Z"/></clipPath>
</defs>
<rect width="1600" height="520" fill="url(#g-cielo)"/>
<circle cx="1230" cy="160" r="260" fill="url(#g-sole)"/>
<circle cx="1230" cy="160" r="46" fill="var(--sole)" opacity=".75"/>
<g data-nuvole></g>
<g data-lontano></g>
<g data-pontile></g>
<!-- under the water -->
<path data-acqua fill="url(#g-acqua)" d="M0 470H1600V900H0Z"/>
<g clip-path="url(#c-sotto)">
<use href="#lontano-forme" data-riflesso opacity=".22" transform="translate(0 940) scale(1 -1)"/>
<g data-strisce></g>
<g data-raggi></g>
<g data-fondale></g>
<g data-caustiche></g>
<g data-alghe></g>
<g data-gambi></g>
<g data-gambero></g>
<g data-pesci></g>
<g data-particelle></g>
<g data-sotto-superficie></g>
</g>
<g data-rive-sotto></g>
<!-- the lily pads, the otter in front of them, the kingfisher, the bubbles, then the water veil that tints everything under the surface -->
<g data-ninfee></g>
<g data-lontra></g>
<g data-martin></g>
<g data-bolle></g>
<path data-velo fill="url(#g-velo)" d="M0 470H1600V900H0Z"/>
<!-- on the surface and above it -->
<path data-luccichio fill="none" stroke="var(--schiuma)" stroke-width="2.2" stroke-linecap="round" opacity=".55"/>
<g data-riverbero></g>
<g data-cerchi></g>
<g data-schizzi></g>
<g data-salice></g>
<g data-canne></g>
<g data-erba></g>
<g data-libellula></g>
</svg>
<header class="testata">
<p class="marchio">
<svg viewBox="0 0 32 32" aria-hidden="true"><path d="M3 20c4-4 8-4 13 0s9 4 13 0M3 26c4-4 8-4 13 0s9 4 13 0M10 13c2-5 9-7 13-3"/></svg>
Holt
</p>
<p class="sotto">River otter reserve</p>
</header>
<div class="testo">
<h1 class="titolo">Wild water, <em>close up.</em></h1>
<p class="riga">Watch the otters fish, play and dive along a mile of quiet river. Open dawn to dusk.</p>
<div class="azioni">
<a class="pillola" href="#visit"><span>Plan a visit</span></a>
</div>
</div>
<p class="aiuto" data-aiuto>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2 15c2.5-2.5 5-2.5 7.5 0s5 2.5 7.5 0 3.5-2 5 -.5M6 9.5a6 6 0 0 1 12 0"/></svg>
<span>Move near the water</span>
</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.
45 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 otter in the river” from Animorio and fit it into my sitesuperficie(x, t) adds two sine waves and the waves left by splashes, which spread and die out. That line closes three shapes: the water behind, the clip for what lies below (reflection, rays, bed) and a translucent veil in front of the otter. So whatever is below the line takes the colour of the river, even the half of the floating otter.
The otter is a group of parts: head, legs, webbed feet and tail each rotate around their joint (translate to the joint, then rotate). Reeds and eel grass are blades that start from a fixed base and bend towards the tip; the cattail head sits on the curve at the right point and takes its tangent. The lily pads ride the surface and their stems always reach the bed.
Two drawings of the otter: on its back and swimming. The dive is a timeline: the otter turns, dips its nose, the splash hides the swap between the two drawings, then a value p from 0 to 1 takes it under the water along an arc (a sine) and turns it with the slope; at the end another splash and the rise with back.out.
A splash is a crown that grows and fades, drops with gravity that fall back and make rings, a wave along the surface and a push to the lily pads nearby. Bubbles rise with a wobble and pop at the surface.
Near the water the pointer leaves rings now and then and bends the reeds nearby; the floating otter turns towards it, paddles over and looks, and the dive waits while it stays curious.
Almost the whole scene is drawn once at the start (hills, trees, layered banks, mossy stones, boardwalk, riverbed). On every frame only what moves is worked out again: the surface, the caustics (six wobbling points per cell), the specks, the eel grass, the willow, the reeds, the fish, the crayfish, the birds and the otter. No filters: light and shade are gradients and strokes.
A small state machine with GSAP timelines: it floats and taps the stone, rubs its face with its paws (the stone waits on the shell), dives, swims, comes up dripping. Every other time it comes up with a fish in its mouth: it holds it in its paws and eats it in three bites, then rolls over (the belly-up drawing and the back-up drawing swap halfway round) and dives again.
| Name | File | Default | What it changes |
|---|---|---|---|
FLOAT_TIME | script.js | 2.6 | Seconds the otter floats and taps its stone before doing something else (rubbing its face, diving). |
SWIM_TIME | script.js | 2.8 | Seconds of swimming under the water. |
DEPTH | script.js | 190 | How deep the dive goes, in scene units (1600 by 900). |
WAVES | script.js | 1 | How high the little waves on the surface are (0 = still water). |
SWAY | script.js | 1 | How much the reeds, the willow, the grass, the ferns and the eel grass sway. |
RIPPLES | script.js | 1 | How big the rings are that the pointer leaves on the water. |
CURIOUS | script.js | 1 | How fast the otter paddles over to the pointer (0 = it never comes). |
As the opening of a nature reserve, a park, an aquarium or a zoo, a wildlife charity or a hiking guide; in general when the first screen should show a living place and make people want to touch it.
The same animations with the sizes adapted: the title goes across the top, the river fills the rest and the otter dives and comes up inside the part that can be seen, measured again on every resize. A finger near the water makes rings and calls the otter like the pointer.
No waves, dives, splashes, bubbles or motion: the scene stands still, with the otter floating in its place; the words appear without moving.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Bricolage Grotesque (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 27 KB; the libraries: GSAP 28 KB.
Tested on 22 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.