
The inchworm menu
Menus, Hover, Transitions
NewFree
The top bar of a small synthesizer maker's site, with the links on the glass of an oscilloscope: under the words runs a trace of green phosphor, flat with its faint fuzz, and under the current page it draws a wave of its own, a different one for each link. Choosing another link the wave slides along the trace and turns into the new one, loses its sync and doubles on the way, and locks as it arrives; the phosphor leaves a wake.
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 oscilloscope nav</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/martian-mono@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the top bar of a small maker of synthesizers. The links sit on the glass of an oscilloscope: under them runs a
trace of green phosphor, flat with its faint fuzz, and under the page you are on it draws a wave of its own.
Choosing another link the wave slides along the trace and changes shape, losing its sync on the way before it
locks again; pointing at a link stirs the trace under it -->
<header class="bar" data-bar>
<a class="brand" href="#" aria-label="Ondine, home">
<svg class="brand__mark" viewBox="0 0 26 20" aria-hidden="true">
<rect x="1" y="1" width="24" height="18" rx="5"/>
<path d="M5 10.5c1.6 0 1.9-4.5 4-4.5s2.4 8 4.5 8 2.3-6 4.2-6c1.2 0 1.5 2.5 3.3 2.5"/>
</svg>
<span class="brand__name">Ondine</span>
</a>
<nav class="screen" aria-label="Main" data-screen>
<div class="screen__scroll" data-strip>
<div class="track" data-track>
<!-- the trace: its halo and the line itself, drawn by the script -->
<canvas class="trace trace--glow" aria-hidden="true" data-glow></canvas>
<canvas class="trace" aria-hidden="true" data-trace></canvas>
<ul class="list" data-list>
<li><a class="link" href="#" aria-current="page" data-link><span class="link__word">Instruments</span></a></li>
<li><a class="link" href="#" data-link data-autoclic><span class="link__word">Modules</span></a></li>
<li><a class="link" href="#" data-link data-autoclic><span class="link__word">Sounds</span></a></li>
<li><a class="link" href="#" data-link data-autoclic><span class="link__word">Manuals</span></a></li>
<li><a class="link" href="#" data-link data-autoclic><span class="link__word">Support</span></a></li>
</ul>
</div>
</div>
<span class="screen__fade screen__fade--l" aria-hidden="true"></span>
<span class="screen__fade screen__fade--r" aria-hidden="true"></span>
<!-- the curved glass over the tube, catching a little light -->
<span class="screen__glass" aria-hidden="true"></span>
</nav>
<a class="cta" href="#" data-cta><span class="cta__bg" aria-hidden="true" data-cta-bg></span><span class="cta__label">Pre-order</span></a>
</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>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.
32 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 oscilloscope nav” from Animorio and fit it into my siteThe glass is a nav set into the bar with its overflow hidden: three CSS backgrounds on top of each other make the tube (a radial gradient of the darkest green), the divisions every 40 pixels (a repeating-linear-gradient) and the axis, a single line at the height --axis which the script reads; a ::before adds the fine ticks every 8 pixels. Over everything a span with the sheen and the darker rim of the curved glass. The links are as tall as the glass, with the word at the top: the trace runs under it, in the low band, and never touches the letters.
Each wave is a function of the phase in cycles, measured from the left edge of its window, that starts and ends at zero: sine, square (the tanh of 7 times the sine, steep but finite edges), sawtooth with a fall of 6 per cent of the cycle, triangle, and a frequency modulated wave sin(3·2πp + 2.2·sin 2πp). A link's window is as wide as the word plus 12 pixels (at least 64) and its edges rise over 9 pixels with a smoothstep; the wave is the sum of the functions weighted by mix, and the wavelength comes from the weighted cycles: so between two waves the change is continuous.
At every frame the height of the trace is worked out every half pixel, with a smooth noise (random knots every 6 pixels, interpolated, plus a little grain). The beam crosses the glass at an even speed, so a bit longer than its step was drawn faster and gets less light: the light of each bit is the step divided by its length, to the power 0.85, at least 0.2, and the edges of the tube are a little dimmer. The bits are sorted into seven levels of light and each level is stroked as one path, with a second, thinner, almost white line on the three strongest.
The canvas is black and is added to the glass with mix-blend-mode: screen. At every frame it is not cleared: the whole of it is darkened by exp(-dt/PERSISTENCE) and the new trace is drawn on it with the lighten operation, so each pixel takes the brighter of the old one fading and the new one: the phosphor lights up at once and dies away slowly, and whatever moves leaves a wake, with the same light at any frame rate. The halo is a copy of the same canvas at half resolution, blurred with the CSS filter blur(GLOW).
Choosing a link a GSAP timeline takes the window (middle and half width) under the new word in TRAVEL seconds with power3.inOut, the weights of the waves to the new one, and lowers the amplitude a little. Meanwhile the sync is lost: the phase rolls at up to 2.2 cycles a second times SYNC_LOSS and the trace is swept twice more at random phases, fainter, and the wave doubles. At 90 per cent of the way the phase snaps to the nearest whole cycle with back.out (the trigger catching) and the amplitude goes back to 1 with an elastic. A new click halfway starts from wherever things are.
The loop on GSAP's ticker runs in full while something moves and for five persistences after; then it wipes the leftovers of the wake and draws the trace clean. With the noise on it goes on at 30 frames a second, but stops when the bar leaves the screen (IntersectionObserver) or the tab is hidden (visibilitychange); with NOISE at 0 it draws once and sleeps. Under 760 pixels the links scroll sideways inside the glass, with the edges darkening on the side of the hidden links, and the link chosen comes to the middle. The links are links in a nav with aria-current, and the arrows, Home and End move among them.
| Name | File | Default | What it changes |
|---|---|---|---|
WAVES | script.js | ['sine', 'square', 'fm', 'saw', 'triangle'] | The wave drawn under each link, in order: sine, square, saw, triangle or fm (frequency modulated). |
AMPLITUDE | script.js | 8 | How far the wave rises and falls from the trace, in pixels. |
PERSISTENCE | script.js | 0.2 | How long the phosphor keeps glowing after the beam has gone, in seconds: higher, longer wakes. |
TRAVEL | script.js | 1 | How long the wave takes to move under the new link, in seconds. |
SYNC_LOSS | script.js | 1 | How badly the trace loses its sync on the way: the wave rolls and doubles before it locks (0: not at all). |
EXCITE | script.js | 1.3 | How much the trace ripples under a link pointed at, in pixels. |
NOISE | script.js | 0.3 | The fuzz of the trace at rest, in pixels (0: a still picture, drawn again only when something changes). |
GLOW | script.js | 3 | How far the halo round the trace spreads, in pixels. |
For the top bar of a site about sound and electronics: a maker of synthesizers or pedals, a recording studio, an electronic music label, a shop for test instruments, an electronics course, a sound designer's site. The trace says where you are even when still, and each page has its own wave.
Under 760 pixels the bar is 58 pixels tall and as wide as the screen less 32 (358 at 390); under 480 only the mark is left, without the name, and the button gets narrower. The five links do not all fit: they scroll sideways inside the glass with a finger, and the edge darkens on the side where there are more. The trace scrolls with them, the graticule stays still on the glass. Touching a link the trace under it stirs while the finger is down, and the link chosen scrolls to the middle of the glass as the wave reaches it.
No sliding: choosing a link the trace fades out and lights up again with the wave already under the new link. On arrival the trace fades in, already whole. No moving noise (the trace is a still picture), no ripples under the pointer, no jolts; the button does not widen or swell and nothing shrinks when pressed.
GSAP 3.15.0, Martian Mono (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 11 KB; the libraries: GSAP 28 KB.
Tested on 8 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.