Skip to content
animorio
IT
Back to the library

The falling letter

As you scroll, a huge word repeats itself in steps and loses its last letter: the I tips over, falls under the word and, as it touches the ground, becomes the button to write.

Scroll, Text, TransitionsGSAP, CSS onlyMediumFree

Scroll inside the preview

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN.

<!doctype html>
<html lang="it">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>La lettera che cade</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/standard.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/big-shoulders@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main>
    <section class="scena" data-scena>
      <h1 class="nascosto">Scrivi</h1>
      <p class="cenno" data-cenno>Scorri piano: la parola si ripete a scalini e perde l’ultima lettera.</p>
      <!-- sei copie della stessa parola, una sopra l'altra: l'ultima è davanti, e la sua I è un link -->
      <div class="eco" data-eco>
        <div class="eco__misura" aria-hidden="true" data-misura><span class="l">S</span><span class="l">C</span><span class="l">R</span><span class="l">I</span><span class="l">V</span><span class="l">I</span></div>
      <div class="copia" data-copia>
        <span class="copia__lastra"></span>
        <span class="copia__parola" aria-hidden="true"><span class="l">S</span><span class="l">C</span><span class="l">R</span><span class="l">I</span><span class="l">V</span><span class="l">I</span></span>
      </div>
      <div class="copia" data-copia>
        <span class="copia__lastra"></span>
        <span class="copia__parola" aria-hidden="true"><span class="l">S</span><span class="l">C</span><span class="l">R</span><span class="l">I</span><span class="l">V</span><span class="l">I</span></span>
      </div>
      <div class="copia" data-copia>
        <span class="copia__lastra"></span>
        <span class="copia__parola" aria-hidden="true"><span class="l">S</span><span class="l">C</span><span class="l">R</span><span class="l">I</span><span class="l">V</span><span class="l">I</span></span>
      </div>
      <div class="copia" data-copia>
        <span class="copia__lastra"></span>
        <span class="copia__parola" aria-hidden="true"><span class="l">S</span><span class="l">C</span><span class="l">R</span><span class="l">I</span><span class="l">V</span><span class="l">I</span></span>
      </div>
      <div class="copia" data-copia>
        <span class="copia__lastra"></span>
        <span class="copia__parola" aria-hidden="true"><span class="l">S</span><span class="l">C</span><span class="l">R</span><span class="l">I</span><span class="l">V</span><span class="l">I</span></span>
      </div>
      <div class="copia copia--davanti" data-copia>
        <span class="copia__lastra"></span>
        <span class="copia__parola" aria-hidden="true"><span class="l">S</span><span class="l">C</span><span class="l">R</span><span class="l">I</span><span class="l">V</span><span class="l" data-i>I</span><span class="copia__base" data-base></span></span>
        <a class="goccia" href="#scrivici" data-goccia aria-label="Scrivici" tabindex="-1"><span class="goccia__premi" data-premi><span class="goccia__corpo" data-corpo><span class="goccia__testo" data-etichetta></span></span></span></a>
      </div>
      </div>
    </section>
    <section class="coda" id="scrivici">
      <h2 class="coda__titolo">Scrivici</h2>
      <p class="coda__testo">Una riga basta. Qui va il modulo, o l’indirizzo a cui scrivere.</p>
    </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="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrambleTextPlugin.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

What it does

The scene holds still and the word SCRIVI (write), as wide as the screen, starts repeating itself: six copies one on top of the other, each smaller and lower than the one before and resting on a plate the colour of the page, so only the tops of the copies behind remain, like steps. Then the last I comes loose: it tips faster and faster, slides under the word and lies down. Touching the ground it squashes and bounces, turns blue and SCRIVICI (write to us) appears on it. From there it is a real button: on mouse over the name reshuffles, and a click leads to the bottom of the page. Scrolling back up, the I stands up again and turns back into a letter.

How it works

Six copies and the plates

The copies are six divs on top of each other with the same word, and each has a plate behind it the colour of the page, a little larger than the word. The timeline takes copy i to scale 1 − i/13 and moves it down by 2i% (on phones 1 − i/12 and 10i%), with the origin at the bottom: the copies behind stay larger, and only their tops show.

The measured I

In the front copy the I is hidden and a link takes its place: a bar of ink laid exactly over the ink of the letter. The box comes from measureText on a canvas (actualBoundingBoxLeft, Right and Ascent) and the baseline from a zero-height span aligned to the baseline.

The fall

In the same timeline, after a pause, the bar rotates 90° with power2.in, drops with power3.in and slides left with sine.in: it speeds up like something tipping over, and hits the ground at full speed. Meanwhile it widens to at least 46 real pixels, because on the ground a finger will have to press it.

Touching the ground

When the timeline passes the landing point, a timed animation starts, not tied to scrolling: the bar squashes (0.72 by 1.12) and springs back with elastic.out, turns blue and ScrambleText writes the name on it. Going back, the opposite happens, and the link leaves the Tab order.

Values to change

NameFileDefaultWhat it changes
ETICHETTAscript.jsSCRIVICIThe button’s name. The word lives in the HTML, letter by letter: if you change it, the last one must stay a stem letter, like the I.
CORSAscript.js1.8How many screens of scrolling the scene lasts.
DITOscript.js46The minimum thickness of the button on the ground, in real pixels.
--blustyle.css#2B45FFThe colour the I takes when it becomes a button.

When to use it

To close a page: the contact word turning into the contact button. It works with any word that ends in a stem letter.

On phones

The copies step down further, 10% each, to fill a tall screen, and the bar on the ground widens to a thickness comfortable for a thumb.

With reduced motion

No held scene and no fall: the word is already in steps and the I is already on the ground, blue, with its name.

Pitfalls

  • getBoundingClientRect on a letter gives the line box, not the ink: to lay the bar over the I you need measureText, and the baseline must be read from a zero-height element.
  • The copies carry GSAP transforms: measure with offsetLeft and offsetTop, which ignore them, and measure again on every ScrollTrigger refresh (refreshInit).
  • The bounce must not be tied to scrolling, or a slow scroller sees it in slow motion: it is fired on a timer when the timeline passes the landing point.
  • While it is still a letter, the link must get neither Tab nor clicks: tabIndex −1 and pointer-events: none until it lands.

Libraries

GSAP 3.15.0, Big Shoulders (font variabile) 5.3.0, Mona Sans (font variabile) 5.3.0.

Weight: 16 KB of our own files.

Testing

Tested on 27 September 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: Done

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