Skip to content
animorio
IT
All components

The sailboat nav

Navigation, Physics, Hover, TextCanvas, GSAPAdvancedCompressed weight: Demo 13 KBGSAP 28 KBFreeDownload the zip

The top bar of a sailing school, with a strip of water along its bottom and a small sailboat lying under the page you are on. Choosing another link the boat turns about, fills its sail and runs there, and its wake rolls along the strip, bounces off the ends and rocks it once it has stopped; the letters of the links ride the waves that pass. The water is a real wave equation, worked out cell by cell. On a phone the links open in a panel of moorings, one for each link, and the boat sails out of one and into another.

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>The sailboat nav</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the top bar of a sailing school. Along the bottom of the bar runs a strip of water, worked out as a real wave
         equation, and a small sailboat lies under the page you are on. Choosing another link the boat turns, fills its
         sail and runs there, leaving a wake that rolls along the strip, bounces off the ends and rocks it when it has
         stopped; the words ride the waves that pass under them. Pointing at a link drops a ripple under it. On a phone
         the links open in a panel of moorings, one strip of water each, and the boat sails out of one and into another -->
    <header class="bar" data-bar>
      <canvas class="sea" aria-hidden="true" data-sea></canvas>

      <a class="brand" href="#" aria-label="Halyard Sailing School, home">
        <svg class="brand__mark" viewBox="0 0 22 24" aria-hidden="true">
          <path class="brand__pole" d="M4 2v20"/>
          <path class="brand__flag" d="M5 3.2l13 4.6L5 12.4z"/>
        </svg>
        <span class="brand__name">Halyard</span>
      </a>

      <nav class="nav" id="halyard-links" aria-label="Main" data-nav>
        <canvas class="nav__sea" aria-hidden="true" data-panel-sea></canvas>
        <ul class="list" data-list>
          <li class="item"><a class="link" href="#" aria-current="page" data-link><span class="link__word" data-word>Lessons</span></a></li>
          <li class="item"><a class="link" href="#" data-link data-autoclic><span class="link__word" data-word>Fleet</span></a></li>
          <li class="item"><a class="link" href="#" data-link data-autoclic><span class="link__word" data-word>Regattas</span></a></li>
          <li class="item"><a class="link" href="#" data-link><span class="link__word" data-word>Clubhouse</span></a></li>
          <li class="item"><a class="link" href="#" data-link data-autoclic><span class="link__word" data-word>Contact</span></a></li>
        </ul>
      </nav>

      <a class="cta" href="#" data-cta><span class="cta__bg" aria-hidden="true" data-cta-bg></span><span class="cta__label">Book a lesson</span></a>

      <button class="toggle" type="button" aria-expanded="false" aria-controls="halyard-links" data-toggle>
        <svg class="toggle__icon" viewBox="0 0 24 24" aria-hidden="true">
          <path class="toggle__line" d="M4 9 Q7 7 10 9 T16 9 T22 9" data-line/>
          <path class="toggle__line" d="M4 15 Q7 13 10 15 T16 15 T22 15" data-line/>
        </svg>
        <span class="toggle__label" data-toggle-label>Menu</span>
      </button>
    </header>
  </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.

30 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 sailboat nav” from Animorio and fit it into my site

What it does

On a white background a night blue bar 64 pixels tall with round corners: on the left a small red flag and the name Halyard, in the middle five links, Lessons, Fleet, Regattas, Clubhouse and Contact, on the right the yellow button Book a lesson. Along the whole bottom of the bar runs a strip of water 10 pixels deep, light blue at the surface and darker below, with a line of light along it. Under the current link floats a small sailboat: a white hull with a red stripe, a cream mainsail, a sand coloured jib and a red pennant at the masthead. On arrival the boat comes in from the left edge and stops under the page you are on. Choosing another link the boat, if it has to go back, turns about: its shape narrows and opens again the other way round. Then it gathers way, the sail fills, the pennant streams aft and the bow lifts; arriving it slows down, the bow dips, it goes a little too far and comes back to its place. The hull running through the water presses it down: behind and in front of the boat crests and troughs rise and run on along the strip, bounce off the ends of the bar and come back, and when they pass under the boat at rest they lift it and make it pitch. The letters of the links ride the water too: they rise on the crests and lean with the slope of the wave under them. With the mouse on a link a drop falls under the word: a ripple spreads from there, lifting its own letters first and then its neighbours'. The name makes the little flag flutter, the button swells with a bounce and its words widen. When the water and the boat are still, nothing is drawn.

How it works

The water is a wave equation

The strip is a row of cells every 3 pixels, each with its height and its speed. At every step each cell is pulled towards its neighbours (c² times the second difference of the heights, over dx²: the waves run at WAVE_SPEED pixels a second), slowed by a friction set by CALM, by a little viscosity on the speeds that keeps the crests smooth and by a very weak spring that brings back the level. At both ends a cell mirrors its neighbour, so the waves bounce off the ends of the bar. The steps are as small as it takes for a wave never to move more than 0.7 of a cell in one (the Courant condition), and never more than 1/30 of a second a frame.

The wake: a Ricker wavelet under the hull

While the boat moves, the cells round it get a push downwards at every step shaped like a Ricker wavelet, (1 − d²)·e^(−d²/2), as strong as the boat's speed times WAKE: a trough under the hull and the water it moves heaped at the bow and the stern. The shape adds up to zero, so the strip keeps its level however far the boat goes (with a plain dip under the hull the water behind it drained away). The equation does the rest: the boat runs faster than the waves and leaves them behind.

The boat on a spring with a brake

The boat's position is a spring towards its mooring, damped at 0.74 with its frequency taken from SAIL_TIME, but the spring's push is capped (2600/SAIL_TIME² pixels a second squared): from far away the boat does not jump, it gathers way, glides and arrives going just a little too far. Its heading is another spring, from 1 to −1: when it goes through zero the shape drawn with scale(face, 1) narrows and opens again turned round, like a boat seen from the side coming about.

The boat reads the water

At every step the boat reads the height of the water at its bow, its stern and its middle: the mean height is the target of a stiff spring (heave, with a little inertia), the slope from bow to stern plus the acceleration (the bow lifts as it speeds up and dips as it brakes) is the angle of another. The sail fills with the speed (the leech is a quadratic curve that bellies out) and the pennant streams aft. The water in front of the hull is drawn again over it with the same gradient as the strip, so the boat sits in it.

Letters on the water

Every word is cut into letters (the link keeps its name in aria-label); the middle of each letter is measured once with offsetLeft, which does not see transforms. Every frame each letter takes translateY(−height·BOB) and a turn from the slope of the water under it, and the style is only written when it changes. The mouse on a link adds a bell to the speeds of the cells under the word: that is the drop, which opens into two waves going both ways.

The panel of moorings on a phone

Below 760 pixels the links go into a panel under the bar, and every row has its own strip of water with its own equation. The Menu button opens the panel with a clip-path coming down, the rows come in one after the other, the water in each row rises with a back.out (so it brims over a little and settles) and the boat comes in from the right edge to its mooring, next to the word. Choosing another row the boat sails out of its mooring to the right and comes into the new one from the right. The waves on the button flatten and turn into a cross, Esc closes and gives the focus back to the button, the arrow keys move along the links.

Values to change

NameFileDefaultWhat it changes
SAIL_TIMEscript.js1.1About how many seconds the boat takes to sail under a new link.
WAKEscript.js1How big a wake the boat leaves behind it (0, none).
WAVE_SPEEDscript.js230How fast the waves run along the water, in pixels a second.
CALMscript.js0.9How quickly the water goes still again: higher, sooner.
RIPPLEscript.js1How big the ripple dropped under a link pointed at is (0, none).
BOBscript.js0.7How much the letters of the links ride the waves that pass under them (0, they stay still).
BOAT_SIZEscript.js1.2The size of the boat.

Try them in the Playground

When to use it

For the header of anyone who deals with the sea and water: a sailing school, a yacht club, a boat or kayak hire, a seaside hotel or holiday homes, a beach club, a site for regattas or boat trips. It suits a few short links on a dark bar; the colours of the water, the boat and the bar change in the variables.

On phones

Below 760 pixels the bar is 60 pixels tall and as wide as the screen less 32 (358 at 390), the yellow button goes and on the right comes the Menu button with two drawn waves. Tapped, a panel of the same blue comes down under the bar with the five links in a column, each with its own strip of water: the rows come in one after the other, the water rises in each row brimming over a little, and the boat comes in from the right and stops next to the current link. Tapping another link the boat sails out of its mooring to the right and comes into the new one from the right; a finger down on a link drops the ripple, like the mouse. The waves on the button turn into a cross and the word Close; Esc closes.

With reduced motion

The water stays flat and still, the letters do not move and no drops fall: choosing another link the boat fades out where it was and fades in under the new link, already at rest. On a phone the panel fades in and out without coming down, and the boat is already at its mooring. The buttons neither press in nor widen.

Pitfalls

  • The first wake was a plain dip under the hull: every trip the strip lost water and behind the boat stayed a trough half a bar long, which the level's spring took seconds to fill. Now the push has the shape of a Ricker wavelet, which adds up to zero: as much as goes down under the hull rises at the bow and the stern.
  • With the current link in bold the word got wider when it became current, but the middles of the letters had been measured before: the letters read the water in the wrong place and the boat stopped off centre. The current link now only changes colour.
  • With the boat 23 pixels tall the masthead, lifted by a crest, touched the tail of the g in Regattas: the water is 10 pixels deep, the words sit 2 pixels higher and the heave of the boat as drawn is capped at 2.6 pixels.
  • With WAVE_SPEED at the end of its range the equation blew up (the wave jumped more than a cell a step): the steps are now split until the wave moves at most 0.7 of a cell in each.
  • The water drawn again in front of the hull, clearer than the strip, made a lighter box round the boat: now it uses the strip's own gradient, a touch clearer only at the surface.
  • The pennant at rest hung straight down and read as a red dot at the masthead: even with the boat still it stays a stretched triangle, which grows longer in the wind of the run.

Libraries

GSAP 3.15.0, Bricolage Grotesque (font variabile, con gli assi del peso e della larghezza) 5.3.0.

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

Testing

Tested on 13 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 components