
The AI generate button
Buttons, WebGL, SVG, Hover, Text
NewFree
In the minimal style: the menu of a small harbour as a radar screen on black. Pressing Menu, the range rings, the cross and the ticks of the bezel are drawn under the button and the beam starts to turn: every link lights up as the beam passes over it and then slowly fades, like phosphor. The pointer takes the beam and holds it on the link you are looking at; the page you are on is in signal orange.
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 radar menu</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the menu of a small harbour, drawn as a radar screen. At rest only the Menu button. Pressed, the range rings
and the bezel are drawn under it and the beam starts to turn: every link lights up as the beam passes over
it and fades again slowly, like phosphor. The pointer (or the finger, or the arrow keys) takes the beam and
holds it on a link; the page you are on is marked in signal orange -->
<div class="radar" data-frame>
<button class="toggle" type="button" aria-expanded="false" aria-controls="radar-menu" data-toggle data-autoclic>
<span class="toggle__icon" aria-hidden="true">
<svg viewBox="-10 -10 20 20" width="20" height="20">
<circle class="toggle__ring" r="8.2"></circle>
<circle class="toggle__ring toggle__ring--in" r="4.2"></circle>
<line class="toggle__beam" data-icon-beam x1="0" y1="0" x2="0" y2="-8.2"></line>
</svg>
</span>
<span class="toggle__words" aria-hidden="true">
<span class="toggle__word" data-word-open>Menu</span>
<span class="toggle__word" data-word-close>Close</span>
</span>
<span class="sr" data-toggle-name>Open the menu</span>
</button>
<nav class="menu" id="radar-menu" aria-label="Main" data-menu inert>
<div class="scope" data-scope>
<svg class="scope__grid" viewBox="-100 -100 200 200" aria-hidden="true" data-grid>
<g data-rings>
<circle class="scope__ring" r="24" pathLength="1"></circle>
<circle class="scope__ring" r="48" pathLength="1"></circle>
<circle class="scope__ring" r="72" pathLength="1"></circle>
<circle class="scope__ring scope__ring--edge" r="96" pathLength="1"></circle>
</g>
<line class="scope__cross" data-cross x1="0" y1="0" x2="0" y2="-96" pathLength="1"></line>
<line class="scope__cross" data-cross x1="0" y1="0" x2="96" y2="0" pathLength="1"></line>
<line class="scope__cross" data-cross x1="0" y1="0" x2="0" y2="96" pathLength="1"></line>
<line class="scope__cross" data-cross x1="0" y1="0" x2="-96" y2="0" pathLength="1"></line>
<g data-ticks></g>
<path class="scope__north" data-north d="M -3 -103 L 0 -98.5 L 3 -103"></path>
</svg>
<div class="scope__sweep" aria-hidden="true" data-sweep></div>
<div class="scope__beam" aria-hidden="true" data-beam></div>
<ul class="blips" data-blips>
<li class="blip" data-blip data-angle="36" data-range="0.6"><a class="blip__link" href="#moorings" data-link>
<span class="blip__mark" aria-hidden="true" data-mark><svg viewBox="-5 -5 10 10"><path d="M -4 0 H 4 M 0 -4 V 4"></path></svg></span>
<span class="blip__text"><span class="blip__word" data-word>Moorings</span><span class="blip__note" data-note>Berths and fuel</span></span>
</a></li>
<li class="blip" data-blip data-angle="122" data-range="0.6"><a class="blip__link" href="#school" aria-current="page" data-link>
<span class="blip__mark" aria-hidden="true" data-mark><svg viewBox="-5 -5 10 10"><path d="M -4 0 H 4 M 0 -4 V 4"></path></svg></span>
<span class="blip__text"><span class="blip__word" data-word>School</span><span class="blip__note" data-note>Learn to sail</span></span>
</a></li>
<li class="blip" data-blip data-angle="196" data-range="0.5"><a class="blip__link" href="#boatyard" data-link>
<span class="blip__mark" aria-hidden="true" data-mark><svg viewBox="-5 -5 10 10"><path d="M -4 0 H 4 M 0 -4 V 4"></path></svg></span>
<span class="blip__text"><span class="blip__word" data-word>Boatyard</span><span class="blip__note" data-note>Repairs, winter</span></span>
</a></li>
<li class="blip" data-blip data-angle="264" data-range="0.5"><a class="blip__link" href="#tides" data-link>
<span class="blip__mark" aria-hidden="true" data-mark><svg viewBox="-5 -5 10 10"><path d="M -4 0 H 4 M 0 -4 V 4"></path></svg></span>
<span class="blip__text"><span class="blip__word" data-word>Tides</span><span class="blip__note" data-note>Today and weekly</span></span>
</a></li>
<li class="blip" data-blip data-angle="330" data-range="0.36"><a class="blip__link" href="#contact" data-link>
<span class="blip__mark" aria-hidden="true" data-mark><svg viewBox="-5 -5 10 10"><path d="M -4 0 H 4 M 0 -4 V 4"></path></svg></span>
<span class="blip__text"><span class="blip__word" data-word>Contact</span><span class="blip__note" data-note>Harbour office</span></span>
</a></li>
</ul>
</div>
</nav>
</div>
</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.
31 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 radar menu” from Animorio and fit it into my siteThe rings and the cross are SVG circles and lines with pathLength 1 and stroke-dasharray 1 1: the timeline takes stroke-dashoffset from 1 to 0, the rings 0.09 seconds apart from the centre to the edge. The bezel's 72 ticks are made by the script (one every 5 degrees, longer every 30) and appear clockwise with a stagger of DRAW·0.7/72. The stroke width comes from the diameter (200/d viewBox units), so it stays one pixel at any size.
The beam is a one pixel div with its transform-origin at the bottom, the trail a round div with a conic-gradient from transparent at 292 degrees to a pale veil at 360: both turn by the same angle, written on every frame in a requestAnimationFrame loop with its step capped at 1/30 of a second. The first turn runs 2.6 times faster and slows down with power2.out to SWEEP seconds a turn.
Every link has a light from 0 to 1: when the beam comes within BEAM degrees it goes up to 1, then drops by dt/FADE every frame. The light drives the opacity (from REST to 1), Mona Sans' weight and width in CSS variables read by font-variation-settings, the note underneath and the turn of the cross. Until the beam finds it for the first time a link is invisible and cannot be clicked.
Inside the screen the pointer's angle (atan2 from the centre) becomes the beam's target, and the beam goes there on a spring: its speed grows by SPRING[0] times the distance and drops by SPRING[1] times the speed, always the short way round. Over a link, or with the keyboard focus on it, the target is the link's angle and its light stays at 1. Leaving, the beam starts again from still and gets back to speed in 0.7 seconds.
The click presses the word to 0.95 and back, moves aria-current (and the orange) and after 0.75 seconds the screen closes in reverse: links and beam go dark, the ticks vanish from the last to the first, the cross and the rings go back to stroke-dashoffset 1 from the edge towards the centre.
The button has aria-expanded and aria-controls, the nav is inert while closed. Enter or Space open it and take the focus to the current page; the arrows move from link to link in the order the beam meets them, Tab goes round the button and the links, Esc closes. The preview's click (detail 0, with no key just pressed) makes a tour: it opens, lets the beam turn, holds it on two links and chooses one.
| Name | File | Default | What it changes |
|---|---|---|---|
SWEEP | script.js | 3.6 | The seconds the beam takes for a full turn. |
FADE | script.js | 2.4 | The seconds a link takes to fade after the beam has passed it. |
BEAM | script.js | 7 | How many degrees either side of the beam light a link up. |
REST | script.js | 0.3 | How much a link shows between two passes of the beam (0 to 1). |
WEIGHT | script.js | [300, 640] | The weight of a word in the dark and right under the beam (100 to 900). |
WIDTH | script.js | [82, 114] | The width of a word in the dark and under the beam (75 to 125). |
SPRING | script.js | [150, 18] | The spring that takes the beam to the pointer: how stiff, and how much it is held back. |
DRAW | script.js | 1.1 | The seconds the rings take to be drawn when the menu opens. |
For the menu of anyone dealing with the sea, navigation and instruments: a marina, a sailing school, a boatyard, a tides or weather app, a studio making surveying tools or software. It suits four, five or six short links on a dark background; the note underneath says in a few words what lies behind it.
The screen takes its diameter from the window's width and height (between 170 and 380 pixels), so the button and the open radar always fit; the words shrink with the screen, and below a 300 pixel diameter the notes are left out. The same animations: the beam turns and lights the links, and a finger on the screen takes it where you want; a tap on a link chooses it.
No turning beam and no rings being drawn: opening, the screen and all the links fade in, already half lit; under the pointer or with the focus a link gains weight and width. Closing, everything fades out.
GSAP 3.15.0, Mona Sans (font variabile, con gli assi del peso e della larghezza) 5.3.0.
Compressed with gzip, the three demo files weigh 8.3 KB; the libraries: GSAP 28 KB.
Tested on 23 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.