Skip to content
animorio
IT
All components

The satin nav

Navigation, WebGL, Physics, HoverWebGL, GSAP, CSS onlyAdvancedCompressed weight: Demo 7.2 KBGSAP 28 KBFreeDownload the zip

The top bar of a fashion house cut from a strip of black satin. Under the page you are on the cloth is gathered, as if pinched from below: the folds run in towards the word from both sides and the light slides along them in long bright streaks. Choosing another page the gather lets go a little, runs there on a spring and pulls the cloth together again, and a soft ripple runs out of the new gather and dies away. Under the link the pointer is on the cloth gathers slightly. The satin is drawn in WebGL.

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 satin nav</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bodoni-moda@5.3.0/opsz.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the top bar of a fashion house, Velin, cut from a strip of black satin. Under the page you are on the satin is
         gathered, as if pinched from below: its folds run in towards the word and the light slides along them. Going
         to another page the pinch lets go and gathers the cloth again further along, the folds travel with it and a
         soft ripple runs out of the new gather. The satin is drawn in WebGL: a height map of folds, lit with the
         stretched highlights of a woven fabric. The links on top are the real navigation -->
    <header class="bar" data-bar>
      <canvas class="bar__satin" aria-hidden="true" data-satin></canvas>
      <a class="brand" href="#" aria-label="Velin, home" data-brand>
        <span class="brand__word">Velin</span>
        <span class="brand__mark" aria-hidden="true">V</span>
      </a>
      <nav class="nav" aria-label="Main">
        <ul class="list">
          <li><a class="link" href="#" aria-current="page" data-link>New in</a></li>
          <li><a class="link" href="#" data-link data-autoclic>Women</a></li>
          <li><a class="link" href="#" data-link>Men</a></li>
          <li><a class="link" href="#" data-link data-autoclic>Atelier</a></li>
          <li><a class="link" href="#" data-link data-autoclic>Journal</a></li>
        </ul>
      </nav>
    </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.

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

What it does

On a black background a bar 60 pixels tall and 760 wide, with barely rounded corners, made of black satin: all along the strip the cloth falls in long soft folds with pale lilac highlights. On the left the name Velin in spaced Bodoni capitals, on the right five links in ivory Bodoni: New in, Women, Men, Atelier and Journal. Under the link you are on the cloth is gathered: from both sides the folds run towards the word, closer together near it, and a streak of light runs along each. On arrival the bar appears smooth, the links rise one after the other, then the cloth gathers under New in and a ripple spreads from the spot. Clicking another link the gather loosens as it runs, with the folds following it across the cloth, goes just past the new link and comes back, pulls tight again and from there a ripple spreads and fades; the chosen link turns full ivory, the others stay dimmer. With the mouse on another link the cloth under it gathers a little and the link lights up; from the keyboard the focused link has a thin outline and the same small gather, and the arrow keys move from link to link.

How it works

A height map

The satin is a single WebGL pass over the whole bar. For every pixel the shader works out how high the cloth is: a long drape made of three almost vertical waves, and the folds of the gather. The slope, taken from the differences between neighbouring pixels, gives the surface's normal, and the shadow and the light come from that.

Folds running towards the word

The folds of a gather are ridges that close in towards the pinched point from both sides: the shader uses the ratio between the vertical and the horizontal distance from the point, so the ridges are close together near the word and open out further away. Their depth grows out of the point and then fades within REACH pixels, and the drape round it is pulled towards the gather, so the long folds bunch up there too. The horizontal distance is softened (a square root instead of the absolute value), or a straight seam was left in the middle.

The light of satin

The light comes from the top left. The highlight is in two lobes, a narrow one and a wide one, and the normal is squeezed horizontally before it is worked out, as for a fabric whose threads run along the bar: so every fold carries a long streak of light. The cloth's colour and the highlight's come from the --satin and --sheen variables.

The gather that runs

The gather's position follows the middle of the chosen link on a spring (SPRING), in steps of 1/120 of a second; its strength on another spring, which drops towards half with the speed: running, the cloth loosens, stopping, it pulls tight. When the gather arrives a ripple starts: a ring that spreads and fades in three seconds. The pointer has a lighter gather of its own (HOVER_GATHER), with its own springs.

Drawing only when needed

The shader runs only while a spring moves, a ripple is alive or the entrance is playing; then the bar stays still without working anything out. Without WebGL the bar falls back to a dark ground with a soft light under the chosen link.

Values to change

NameFileDefaultWhat it changes
FOLDSscript.js13How many folds run into the gather.
DEPTHscript.js9How deep the folds of the gather are, in pixels of the cloth's height.
REACHscript.js120How far from the word the gather pulls the cloth, in pixels.
SHEENscript.js0.9How bright the light on the folds is (0, matt cloth).
SPRINGscript.js[120, 15]The spring the gather runs on: how stiff, and how much it is held back (less, further it overshoots).
RIPPLEscript.js1How strong the ripple that runs out of a new gather is (0, none).
HOVER_GATHERscript.js0.5How much the cloth gathers under the link the pointer is on (0 to 1).

Try them in the Playground

When to use it

For the header of anyone who sells or makes things of cloth and luxury: a fashion house, an atelier, a tailor, a lingerie or tie shop, a perfume brand, an elegant hotel. It suits four to six short links on a dark ground; the satin can be any colour, by changing --satin and --sheen (an ivory with warm highlights, a midnight blue, a bottle green).

On phones

Below 600 pixels the name becomes the V alone and the five links spread across the whole bar, at 14 pixels: at 390 the bar is 358 wide and 60 tall, and they all fit without spilling over. The satin is the same, with the gather and the ripple; with a finger down on a link the cloth gathers under it as with the mouse, and the tap chooses it.

With reduced motion

The cloth does not run or ripple: choosing another link the gather is already under it and the satin is redrawn in a short fade; the pointer lights the link without gathering. When the page opens the bar fades in with the gather already in place.

Pitfalls

  • The first gather was made of rays round the point (the cosine of the angle): it looked like an explosion, a sunburst of very thin lines that blurred into the pixels. Now the folds are ridges running horizontally towards the word, and wider near the point.
  • With the Kajiya-Kay highlight and flat cloth almost at right angles to the threads, the whole bar lit up grey. Now the highlight is narrow (exponents 30 and 140) and the normal is only squeezed horizontally: flat cloth stays black and the folds light up.
  • The drape made of slanted waves gave oval spots of light, like drops on the cloth. Now the waves are almost vertical and the light comes down in streaks, as on a fabric that falls.
  • With the absolute value of the horizontal distance, a sharp vertical seam was left under the word: now the distance is softened with a square root.

Libraries

GSAP 3.15.0, Bodoni Moda (font variabile) 5.3.0.

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

Testing

Tested on 19 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