Skip to content
animorio
IT
Back to the library

The wave made of circles

Scroll, SVGGSAPMediumCompressed weight: Demo 5.7 KBGSAP + ScrollTrigger 45 KBFreeDownload the zip

Scroll inside the preview

The page of a synthesizer oscillator, in the minimal style: as you scroll a chain of circles turns, each on the tip of the one before, and the tip of the last one writes a wave. With one circle it is a pure sine; every new circle pops in with a small spring and the wave gets squarer, until it almost fills a blue dashed square. The word below widens and gains weight with it.

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 wave made of circles</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>
  <!-- the product page of a made-up synthesizer maker, Odalo. Scrolling turns a chain of circles, each spinning on the
       tip of the one before, and the tip of the last one writes a wave. One circle writes a pure sine; every new circle
       pops in on the tip of the chain and the wave gets squarer, until it almost fills a thin dashed square. The word
       under it widens and gains weight with the wave -->
  <main>
    <section class="apertura">
      <p class="piccolo">Odalo, synthesizers</p>
      <h1 class="titolo">Every sound<br>is made of circles.</h1>
      <p class="piccolo piccolo--tenue">Scroll to add them.</p>
    </section>

    <section class="racconto" data-racconto>
      <div class="palco" data-palco>
        <header class="testa">
          <p class="marchio">Odalo</p>
          <p class="piccolo piccolo--tenue">Square One oscillator</p>
        </header>

        <svg class="tavola" data-tavola aria-hidden="true">
          <g data-quadro>
            <line class="asse" data-asse></line>
            <path class="bersaglio" data-bersaglio></path>
            <g data-cerchi></g>
            <line class="filo" data-filo></line>
            <path class="onda" data-onda></path>
          </g>
        </svg>

        <div class="scritta">
          <p class="conto" data-conto aria-live="polite">1 circle</p>
          <p class="parola" data-parola>square</p>
        </div>
      </div>
    </section>

    <section class="apertura apertura--fine">
      <h2 class="titolo titolo--medio">Square One.<br>Out in the spring.</h2>
    </section>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.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.

21 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 wave made of circles” from Animorio and fit it into my site

What it does

The product page of a made-up synthesizer maker, Odalo, on a warm white with thin black lines and a single electric blue. After the opening the screen stops: on the left a big circle with its arm, on the right a graph with a grey axis and a blue dashed square that slowly lights up, the wave to aim for. As you scroll the circle turns and a dotted blue thread carries the height of the arm’s tip to the start of the wave, which runs off to the right: with a single circle it is a soft sine. Then, one at a time, smaller and smaller circles pop out on the tip of the chain (the odd harmonics: a third, a fifth, a seventh…), each with a small spring that makes it grow a bit too much and come back; each one spins faster than the one before, and the wave changes shape before your eyes: it flattens at the top and bottom, its sides straighten, until with eight circles it sits almost inside the square, with the little horns at the corners that every real square wave keeps. At the bottom left the count of circles and the word square, which starts narrow and thin and widens and gains weight with the wave, up to a small final kick when the last circle arrives. On a phone the drawing is turned on its side and the wave runs down the screen. Everything follows the scroll, forwards and backwards.

How it works

A Fourier series in an SVG

A square wave is a sum of odd sine waves: sin(x) + sin(3x)/3 + sin(5x)/5 and so on, times 4/π. Each term is a circle with a radius of 1/n spinning n times faster than the first, resting on the tip of the arm of the one before. The script draws the circles and the arms in an SVG group and, every frame, redraws the wave with 260 points: the height at each point is the same sum worked out at a slightly older phase.

A frame of its own

Everything is drawn in a frame of its own: u along the wave, v its height. On a computer the group is only moved and the wave runs to the right; on a phone, where there is no width, the same group is turned by 90 degrees and the wave runs down the screen, without rewriting anything. The big circle’s radius and the graph’s start and length are worked out again on every ScrollTrigger refresh and on every resize.

The scroll does the turning

The timeline, tied to the scroll with scrub SCRUB, carries a number from 0 to 1 that becomes the phase: the big circle makes TURNS turns over the whole story. Each circle also has a weight w from 0 to 1 that multiplies its radius: new circles arrive with a tween of w on the POP curve, a back.out that takes it a little past 1 and back. Since the wave uses w too, the wave makes the same spring.

The square to aim for and the thread

The dashed square is the sign of the same base sine wave (+1 or −1), drawn with horizontal and vertical strokes where the sign flips, so it runs along with the wave. The dotted blue thread goes from the tip of the last arm to the start of the wave: that is where you see that the wave is written by the circles.

The word that turns square

The word square uses Anybody, whose wdth axis runs from 50 to 150. The sum of the circles’ weights says how square the wave is: with one circle the word is at 50 wide and almost weightless, with all the circles at 150 and full. The count above the word changes when a circle is half grown.

Values to change

NameFileDefaultWhat it changes
CIRCLESscript.js8How many circles at the end: the more there are, the squarer the wave.
TURNSscript.js7How many turns the big circle makes over the whole story.
PERIODSscript.js2How many waves fit in the graph.
POPscript.js'back.out(1.8)'How each new circle grows in: the number in brackets is how far it overshoots before coming back.
TARGETscript.jstrueThe dashed square to aim for.
SCRUBscript.js0.6How many seconds the drawing takes to catch up with the scroll.

Try them in the Playground

When to use it

For the page of a musical instrument, an audio plugin, a maths or physics course, or any product that explains itself by showing how it is built from simple pieces: one circle at a time, until the shape is the right one.

On phones

The same, turned on its side: the circles sit at the top in the middle and the wave runs down the screen, with a vertical thread; the word sits at the bottom and takes almost the full width when it is square.

With reduced motion

Nothing spins and nothing grows: the chain stands still at a fixed phase and each new circle, with its wave, appears at once behind a short cross-fade; the word changes width and weight in steps with the count.

Pitfalls

  • For reduced motion the first version lowered the drawing’s opacity with overlapping timeline tweens on the same value: they were too long and the drawing stayed faded for half the time. Now the fade is worked out in the drawing from the story’s clock, only a tenth before and after each new circle.
  • The target square drawn as a pushed sine (a rounded sign) would have crooked sides: it is drawn from the sign of the base sine wave, with a vertical stroke halfway between the two points where the sign flips.
  • On a phone the word at width 150 and 9vw ran off the screen: there the size is 14.5vw, measured so that a wide, full square fits the width.
  • There is no need to redraw the wave from a list of past points (a trail): working it out again every frame with the circles of now makes the whole wave change shape at once when a circle arrives, and that is what tells the idea.

Libraries

GSAP 3.15.0, ScrollTrigger (GSAP) 3.15.0, Anybody (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 5.7 KB; the libraries: GSAP + ScrollTrigger 45 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 animations