Skip to content
animorio
IT
Back to the library

The opening angle

SVG, TextGSAP, CSS onlyMediumCompressed weight: Demo 7.9 KBGSAP 28 KBFreeDownload the zip

A protractor drawn in hairlines: the free arm turns round the scale, clicks into a notch every fifteen degrees with a hair of bounce, and the number of degrees widens with the angle, narrow at zero and very wide at 180. At ninety degrees the little arc in the corner folds into the small square of a right angle, in the one accent colour. Minimal style.

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 opening angle</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/anybody@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- a protractor drawn with hairlines: one arm is fixed on the base line, the other is dragged round the scale and
       clicks into a notch every fifteen degrees. The number underneath opens with the angle (Anybody's width axis),
       and at ninety degrees the little arc in the corner folds into the square of a right angle -->
  <main class="stage" data-stage>
    <header class="top">
      <span class="top__name">Bevel</span>
      <span class="top__note">An angle, measured</span>
    </header>

    <div class="dial" role="slider" tabindex="0" aria-label="Angle" aria-valuemin="0" aria-valuemax="180" aria-valuenow="0" aria-valuetext="0 degrees" data-dial>
      <svg class="dial__svg" viewBox="-320 -306 640 322" aria-hidden="true">
        <path class="base" pathLength="1" data-base></path>
        <path class="scale" pathLength="1" data-scale></path>
        <path class="ticks" data-ticks></path>
        <path class="ticks ticks--in" data-ticks-in></path>
        <path class="mark" data-mark></path>
        <path class="arm arm--fixed" data-fixed></path>
        <path class="arm" data-arm></path>
        <path class="grip" data-grip></path>
        <path class="pivot" data-pivot></path>
      </svg>
      <div class="labels" aria-hidden="true" data-labels></div>
    </div>

    <div class="read" aria-hidden="true">
      <div class="num" data-num><span class="num__in" data-num-in><span data-digits>0</span><span class="num__deg">°</span></span></div>
      <div class="name" data-name></div>
    </div>

    <p class="hint">Drag the arm round the scale</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>

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 opening angle” from Animorio and fit it into my site

What it does

On warm white paper, at the top left the name of a made-up brand, Bevel, and on the right An angle, measured. In the middle a half circle protractor, all in hairlines: the base line, the arc of the scale, a tick for every degree, longer every five, every fifteen and every forty-five degrees, and the numbers 0, 30, 60, 90, 120, 150 and 180 outside the rim. Two arms start from the middle: one fixed on the base line, towards zero, and a free one with a small thicker grip at its tip. Dragging (anywhere on the page, with the mouse or a finger) the free arm points towards the hand and follows it on a spring. Every fifteen degrees there is a notch: when the arm gets close it is pulled in, goes a hair past and settles; the ticks round it feel the knock and reach in towards the middle with a small shiver, and the number takes on weight for a moment. The ticks inside the angle turn dark, and so do the scale numbers the angle has passed. Underneath, large, the number of degrees in Anybody, a variable typeface whose width goes from 50 to 150: the number opens with the angle, narrow and tall at zero, wide and low at 180, and it follows the arm's bounce too. Under the number the angle's name, zero, acute, right, obtuse, straight angle, which changes letter by letter. At exactly ninety degrees, once the arm has settled, the little arc in the corner folds into the small square of a right angle and turns electric blue, the one accent colour. The arm does not go past the base line: if it hits it, at zero or at 180, it bounces and settles. When the page opens the base line opens from the middle, the scale is drawn from 0 to 180 with the ticks growing one after the other, the arms stretch out from the middle and the number rises from below. When nobody touches it the arm moves by itself: 30, 90 with the square, 150, 180, 45, then it drops back onto zero with a small bounce and starts again; let go by the hand, after a moment it goes back to zero and starts off again. The arrow keys move one degree (fifteen with Shift), Page Up and Page Down fifteen, Home and End go to 0 and 180.

How it works

The protractor in an SVG

The SVG has a viewBox of 640 by 322 with the vertex at the origin and the scale at radius 260. Angles are counted as in geometry, anticlockwise from zero on the right, so a point at d degrees is (r·cos d, −r·sin d): y goes down. The ticks are segments from the scale towards the middle, 4, 9, 16 or 24 long depending on the degree, in two paths redrawn every frame: the ones inside the angle darker, the others lighter. Below 520 pixels wide the one-degree ticks are left out, so they do not make a grey band.

The arm on a spring, with the notches and the stop

The hand asks for an angle (atan2 from the vertex to the pointer, between 0 and 180; below the base line it is 0 or 180). If it is less than MAGNET degrees from a multiple of STEP, the request becomes the notch: that is the magnet that pulls the arm in. The arm goes towards the request on SPRING, integrated by hand on GSAP's ticker in four steps a frame with the time capped at 1/30 of a second; held back only a little, it goes a degree past and comes back. The base line is a stop: if the angle passes 0 or 180 it is put back on the edge and the speed is reversed, multiplied by BOUNCE.

The knock the ticks feel

Every time the request enters a notch (and every time the arm hits the stop) a knock is recorded: where, and how long ago. Each tick stretches by LEAN × e^(−(distance/6.5)²) × e^(−7t) × cos(21t), a bell round the notch that shivers and dies down in under a second. At the same moment the number's weight goes up by WEIGHT[1] and comes back down with a power3.out tween.

The number that opens and the right angle's square

The number's width is font-stretch from WIDTH[0] to WIDTH[1] in proportion to the arm's angle, spring included. The digits show the notch when the arm is less than 0.9 degrees from it, so the settling does not make them dance between 89 and 91. The mark in the corner is a line of 28 points taken twice, on the arc of radius 58 and on the right angle's corner line (A, B, C, which works for angles other than 90 too, as a rhombus); a value m from 0 to 1 blends them point by point, and the colour goes from ink to accent with gsap.utils.interpolate. m rises with back.out only when the request is 90 and the arm has settled.

The opening, the walk on its own and the hands

A GSAP timeline opens the base line from the middle, grows the ticks with a back.out when the sweep reaches them (and draws the scale with stroke-dashoffset), stretches out the arms and raises the number. Then the walk starts: a timeline with repeat −1 that takes the hand's request through the TOUR angles with power2.inOut, holding each one HOLD seconds, and drops back onto zero with power3.in. When the hand presses, the walk stops; let go, after IDLE seconds a gsap.delayedCall takes the arm back to zero and the walk starts again. The protractor is a role=slider with aria-valuenow.

Values to change

NameFileDefaultWhat it changes
STEPscript.js15Every how many degrees there is a notch the arm clicks into.
MAGNETscript.js2.5From how many degrees away a notch pulls the arm in (0, no magnet).
SPRINGscript.js[190, 15]The arm's spring: how stiff, and how much it is held back (less hold, more overshoot).
BOUNCEscript.js0.45How much of its speed the arm keeps when it hits the base line.
LEANscript.js14How far the ticks round a notch reach in when the arm clicks into it.
WIDTHscript.js[56, 148]The width of the number with the arms closed and lying flat (50 to 150).
WEIGHTscript.js[560, 190]The weight of the number (100 to 900), and how much heavier it gets at each click.
TOURscript.js[30, 90, 150, 180, 45, 0]The angles the arm walks through by itself, from 0 to 180.
HOLDscript.js1.1The seconds the arm rests on each angle of the walk (at 90 and 180 one and a half times as long).
IDLEscript.js2.4After how many seconds without hands the arm goes back to zero and starts off again.

Try them in the Playground

When to use it

For a value that is an angle or an opening and should be felt: the tilt of a solar panel or a seat back on a product page, the angle of a gradient or a rotation in a design app, the beam of a spotlight, an architect's or a carpenter's studio introducing itself with its tools. It works where there is room for a single scene; in a form full of fields a normal slider is enough.

On phones

The same animations with the sizes adapted: the protractor is as wide as the screen, the scale numbers smaller, the one-degree ticks left out below 520 pixels, the number of degrees scales with the screen. The arm follows a finger dragging anywhere on the page (the page does not scroll there, it is a single scene) and stays where the finger leaves it; then it goes back to zero and starts off by itself.

With reduced motion

No spring, no bounces, no shivering ticks and no rising letters: the arm goes straight to the angle, the square appears already folded, the angle's name swaps with a short fade. In the walk on its own the arm does not move between one angle and the next: it fades back in already in place. When the page opens everything fades in.

Pitfalls

  • Grown and shrunk with GSAP's scaleX and svgOrigin '0 0', the base line ended up off centre, running to the left edge of the page, and the vertex mark stayed only above the line: line and mark are now redrawn every frame at their size, with no transforms.
  • With the digits taken from the arm's angle, the settling into the 90 notch made them dance between 89, 90 and 91, and the name with them (acute, right, obtuse): the digits show the notch when the arm is less than 0.9 degrees from it.
  • The back.out that folds the arc into the square takes m past 1: blending the points beyond the square made the mark come out crooked, its top side sloping up. m stops at 1 for the shape and whatever is left only makes the square grow a hair.
  • The pause on an angle sits before the next tween, so it is measured on the angle the arm leaves and not on the one it goes to: at first the arm rested longer on 30 than on 90.
  • The one-degree ticks, on a phone, are two and a half pixels apart and turn into a grey band: below 520 pixels of protractor only the ones every five degrees stay.

Libraries

GSAP 3.15.0, Anybody (font variabile) 5.3.0.

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

Testing

Tested on 16 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 animations