Skip to content
animorio
IT
Back to the library

The ticker-tape transition

Transitions, TextGSAP, CSS onlyMediumCompressed weight: Demo 5.9 KBGSAP 28 KBFreeDownload the zip

A minimal page change: on a click every line of the page, the label, the title, the text and the link, shrinks and narrows into one column of small type in the middle of the screen, like the tape of a telex. The tape scrolls up past a thin vermilion head, carrying the old lines away and bringing up those of the new page, which open out into their places gaining width and weight on a spring, one after the other.

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 ticker-tape transition</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage" data-stage>
    <header class="bar">
      <span class="brand">Wire</span>
      <nav class="nav" aria-label="Pages">
        <button type="button" data-go="notes" aria-current="page">Notes</button>
        <button type="button" data-go="rooms">Rooms</button>
        <button type="button" data-go="about">About</button>
      </nav>
    </header>

    <!-- three pages of a made-up sound archive; on a change every line of the page runs into one narrow tape,
         the tape scrolls past a thin head and the lines of the new page open out into their places -->
    <section class="page" data-page="notes" aria-label="Notes">
      <p class="ln ln--kicker" data-role="kicker">Field notes</p>
      <h1 class="ln ln--title" data-role="title">Listen.</h1>
      <p class="ln ln--body" data-role="body">Notes from a studio that</p>
      <p class="ln ln--body" data-role="body">records the quiet parts</p>
      <p class="ln ln--body" data-role="body">of cities at night.</p>
      <a class="ln ln--go" data-role="go" href="#rooms" data-next="rooms" data-autoclic>Hear the rooms<svg viewBox="0 0 16 12" aria-hidden="true"><path d="M1 6h13M9 1.5 14 6l-5 4.5"/></svg></a>
    </section>

    <section class="page" data-page="rooms" aria-label="Rooms">
      <p class="ln ln--kicker" data-role="kicker">Recordings</p>
      <h2 class="ln ln--title" data-role="title">Rooms.</h2>
      <p class="ln ln--body" data-role="body">Twelve rooms, one microphone,</p>
      <p class="ln ln--body" data-role="body">the hum of every ceiling</p>
      <p class="ln ln--body" data-role="body">kept exactly as it was.</p>
      <a class="ln ln--go" data-role="go" href="#about" data-next="about" data-autoclic>Who we are<svg viewBox="0 0 16 12" aria-hidden="true"><path d="M1 6h13M9 1.5 14 6l-5 4.5"/></svg></a>
    </section>

    <section class="page" data-page="about" aria-label="About">
      <p class="ln ln--kicker" data-role="kicker">About</p>
      <h2 class="ln ln--title" data-role="title">Wire.</h2>
      <p class="ln ln--body" data-role="body">A small sound archive</p>
      <p class="ln ln--body" data-role="body">kept by two people</p>
      <p class="ln ln--body" data-role="body">and one long cable.</p>
      <a class="ln ln--go" data-role="go" href="#notes" data-next="notes" data-autoclic>Back to the notes<svg viewBox="0 0 16 12" aria-hidden="true"><path d="M1 6h13M9 1.5 14 6l-5 4.5"/></svg></a>
    </section>

    <!-- the head the tape runs past: a short hairline with a tick at each end -->
    <div class="head" data-head aria-hidden="true"><span class="head__tick"></span><span class="head__tick"></span></div>

    <footer class="foot">
      <span>Wire sound archive</span>
      <span>Listening room open on weekdays</span>
    </footer>
  </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.

37 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 ticker-tape transition” from Animorio and fit it into my site

What it does

The site of a made-up sound archive, Wire, in three pages and almost nothing else: Notes, Rooms and About, each with a label, a huge title in Archivo, three lines of text and a link with a drawn arrow. On load the first page already comes out of the tape. On a click on a link or a menu item the lines of the page run to the middle of the screen, the title first, and become one column of small, condensed type (width 62), one line under the other as on a paper tape. Under the line passing it the head appears, a vermilion hairline with two ticks. The tape scrolls up with a soft start and a brake: the old lines rise and fade, the new ones come up from below and each one, as it passes the head, lights up in vermilion. When the new lines sit in front of the head, the head goes out and every line opens out into its place on the page: it grows to its size, widens and gains weight on a spring, the title first and then the others from top to bottom. On its own the page changes every two and a half seconds, going round Notes, Rooms, About.

How it works

Every line is a block

Every line of the page (label, title, text, link) is a block on a single row (white-space: nowrap), moved only with the x, y and scale transforms, with its origin at the top left. Its width and weight live in the --w and --g variables, tied in the CSS to font-stretch and font-weight through clamp, so GSAP animates them as numbers and the spring never carries the type past the axes of Archivo (62-125, 100-900).

The tape

The tape is a column in the middle of the screen with its rows TAPE_SIZE times 1.9 pixels apart, row zero on the head. To send a line onto the tape, the script measures where it sits at rest (getBoundingClientRect with the transform briefly removed) and works out the move to its place on the tape and the scale, TAPE_SIZE divided by its font size. The old lines gather around the head, the new ones right after them, two and a half rows further on, already small and invisible.

The scroll

The scroll is a tween of one number, from zero to as far as it takes to bring the middle of the new lines onto the head, with power2.inOut; on every update each line is set back to its y from the same formula. Its opacity depends on its distance from the head (it fades above and below) and the line passing over the head turns vermilion. Everything comes from that number, so the editing bench can stop and rewind the scroll.

The page opening out

When the scroll ends, each new line goes back to x 0, y 0 and scale 1 with elastic.out(1, SPRING), and width and weight go back to its own with back.out, staggered by STAGGER: the title first, then the others from top to bottom. At the end transforms and variables are removed from the lines, so the page stays as the CSS has it and a resize finds nothing written by hand.

The loop, phones and reduced motion

A click during a change waits in a queue; a resize takes the change straight to its end. On its own the site goes round the three pages with a gsap.delayedCall of HOLD seconds. On phones the sizes come down (the title with a min of vw and vh) and the tape stays in the middle. With reduced motion the new page fades in over the old one, with no tape.

Values to change

NameFileDefaultWhat it changes
TAPE_SIZEscript.js15The size in pixels of the type on the tape.
TAPE_WIDTHscript.js62The width of the type on the tape, 62 to 125 (the page goes back to its own).
GATHER_TIMEscript.js0.6The seconds the old lines take to run into the tape.
SCROLL_TIMEscript.js0.95The seconds the tape takes to scroll past the head.
OPEN_TIMEscript.js1.1The seconds the new lines take to open out into their places.
STAGGERscript.js0.05The seconds between one line and the next.
SPRINGscript.js0.5The spring of the opening: lower wobbles more, 1 hardly wobbles.
HOLDscript.js2.6The seconds each page stays before the next one comes by itself.

Try them in the Playground

When to use it

To move from one page to another on a quiet site made of words: an archive, a magazine, a newspaper, a sound or design studio, a portfolio. The tape says that the new page is made of the same lines as the old one, only lined up again.

On phones

The same transition with the sizes adapted: the title is sized on both the width and the height of the screen, the tape stays in the middle and the head gets shorter. A tap on the link or the menu changes the page.

With reduced motion

Nothing moves: the new page fades in over the old one with its lines already in place, and there is no tape.

Pitfalls

  • The lines are measured with the transform briefly removed: with GSAP’s transform on them getBoundingClientRect gives their place on the tape, not on the page.
  • The scroll does not animate the lines one by one but a single number, and on every update it sets all the y values again: so the lines never come off the tape and the editing bench can rewind it.
  • The --w and --g variables written by GSAP are not removed by clearProps: at the end they are removed with style.removeProperty, or the line stays condensed on the next page.
  • The head sits under the line passing it, not through it: through the middle it looked like struck-through text.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

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

Testing

Tested on 22 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