Skip to content
animorio
IT
All components

The waitlist with the queue

Forms, PhysicsGSAP, CanvasAdvancedCompressed weight: Demo 9.6 KBGSAP 28 KBFreeDownload the zip

The waitlist of a calendar app, with the queue drawn: 2,480 thin grey marks on a canvas, one for every person waiting. Joining, your green mark drops into the end of the line, its neighbours feel the landing one after the other and a fisheye lens opens over your place, with the number rolling digit by digit. Every friend who joins with your link moves you up ten places: the marks ahead step back and yours slides forward.

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 waitlist with the queue</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the waitlist of a calendar app: every person waiting is a thin mark in the queue. Joining, your mark drops
         in at the end and a lens opens over it; every friend who joins with your link moves you up. Joining is
         pretended: see post() in the script -->
    <div class="wait" data-wait data-autoclic>
      <h2 class="wait__title" id="wait-title">Halden is almost here</h2>
      <p class="wait__sub">A calmer calendar. The first invites go out in November.</p>

      <div class="queue" data-queue>
        <canvas class="queue__canvas" aria-hidden="true" data-canvas></canvas>
        <span class="queue__you" aria-hidden="true" data-you><span>You</span></span>
      </div>
      <p class="wait__count" data-count><span data-count-text>2,480 people are waiting.</span></p>

      <form class="wait__form" action="#" novalidate aria-labelledby="wait-title" data-form>
        <div class="wait__row">
          <div class="wait__field" data-field>
            <label class="sr" for="wait-email">Your email</label>
            <input class="wait__input" id="wait-email" name="email" type="text" inputmode="email" autocomplete="email" autocapitalize="off" autocorrect="off" spellcheck="false" enterkeyhint="send" maxlength="254" placeholder="you@example.com" aria-describedby="wait-status" aria-invalid="false" data-input>
            <span class="wait__caret" aria-hidden="true" data-caret></span>
          </div>
          <button class="join" type="submit" data-join>
            <span class="join__bg" aria-hidden="true"></span>
            <span class="join__label" data-join-label>Join the waitlist</span>
          </button>
        </div>
        <p class="wait__status" id="wait-status" data-status><span class="status__line">One email when your invite is ready. Nothing else.</span></p>
      </form>
      <p class="sr" role="status" aria-live="polite" data-live></p>
    </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>

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.

22 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 waitlist with the queue” from Animorio and fit it into my site

What it does

On a white page the title Halden is almost here and under it A calmer calendar. The first invites go out in November. Below, a dense rectangle of grey upright marks, coming in row after row from the top left, with 2,480 people are waiting. under it; then the you@example.com field and the black Join the waitlist pill, and the line One email when your invite is ready. Nothing else. Pressing the button with a wrong email the field takes a green edge, gives a small jolt sideways and the line says what is missing (The email needs its ending, like .com.). With a valid email the button says Joining, then On the list, the field closes in pale grey; a green mark drops into the end of the line, the marks nearby jump one after the other, and a lens opens over it: the marks around spread and lengthen into a fan as if seen up close, and the label You appears above the green mark. Below reads Your place in line: 2,481 in green, and Copy your link · Use another email. With the link copied, a friend joins soon after: a new mark appears at the end, the ten marks ahead of yours slide back one place, yours slides forward ten with the lens following it, and the number rolls to 2,471. Use another email closes the lens, the green mark leaves and the line closes up again.

How it works

A line of 2,480 marks

Every person is an object with a place in the line (a number that can sit between two places while it moves), a jump and an alpha. The place becomes a cell of a grid of ROWS rows, left to right and top to bottom, with a margin above and below so the lens is never cut. All the marks are drawn on a canvas at the screen’s density, one fillRect per mark; the drawing runs on the GSAP ticker only for the seconds a change asks for, then stops.

The fisheye lens

Within REACH pixels of your place, a point at distance r is pushed out to (d + 1) r / (d r / REACH + 1): in the middle everything is d + 1 times larger and at the edge the lens meets the flat line with no jump. The magnification of each mark also makes it wider, longer and darker. GSAP takes d from 0 to LENS and moves the lens’s centre together with your place.

Landing at the end of the line

Your mark drops from 44 pixels with power2.in and, as it lands, the marks of the same row within seven places get a jump that comes back with elastic.out, staggered by distance: the nearest feel the blow first. The You label is an HTML element the drawing puts over the mark at every frame.

Moving up

When a friend joins (friendJoined(), pretended in the demo after you copy the link) a new mark lights up at the end, the BOOST marks ahead of yours slide back one place, staggered, and yours slides forward BOOST places with the lens following it; the number of your place rolls only in the digits that change, each in its own fixed-width window.

The field and the sending

The email field is real, with a label, aria-invalid and aria-describedby to the line under it, which says what is missing in plain words. Joining is pretended (post(), WAIT seconds); the messages also reach screen readers through an aria-live region. Use another email puts the line back as it was: the marks that had made room for you step forward again.

Values to change

NameFileDefaultWhat it changes
AHEADscript.js2480How many people are already in line.
ROWSscript.js16How many rows the line is drawn in.
LENSscript.js3.2How much the lens magnifies: the middle is 1 + LENS times larger.
REACHscript.js64The radius of the lens, in pixels.
BOOSTscript.js10How many places a friend who joins moves you up.
WAITscript.js0.7How many seconds the pretended joining takes.
FRIENDscript.js1.8In the demo, the seconds between copying the link and the friend joining.

Try them in the Playground

When to use it

On the launch page of an app or a product that is not out yet, where the waitlist is the only thing to do: the drawn line says how many people are waiting better than a number, and the lens makes whoever joins feel like one person among the others. The invite link that moves you up is the engine of word of mouth.

On phones

The block takes the width of the screen; the line narrows (the marks get denser, the same 2,480) and the button goes under the field, as wide as it. The field opens the email keyboard and the Enter key says Send; Copy your link and Use another email are tapped as buttons.

With reduced motion

The line appears whole, without filling in; the green mark does not drop and its neighbours do not jump: it fades in with the lens already open. Moving up, the mark and the lens are at once in their new place and the number changes without rolling.

Pitfalls

  • Your mark is always the last, so in the last row: the lens was cut by the canvas’s edge. The grid now has a margin above and below; the lens stays a dome, because there is nobody behind you.
  • While a digit rolled, the old one left the flow and the new one was still absolutely placed: for a moment the digit’s window was zero wide and it read 2,41. The window’s width is now fixed before rolling.
  • With 20 rows of 3-pixel marks in a light grey, the line read as a texture rather than people: it is now 16 rows, taller marks and a darker grey.

Libraries

GSAP 3.15.0, Instrument Sans (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 9.6 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