Skip to content
animorio
IT
Back to the library

The word under the film

Hover, TextCanvas, GSAPMediumCompressed weight: Demo 8.5 KBGSAP 28 KBFreeDownload the zip

In the minimal style: a page of thin lines with a word hidden in it, and on top a black film full of slits. The pointer slides it and turns it: askew, wide moiré bands run between the slits; level, it clicks into register and the word lights up and gains weight.

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 word under the film</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage" data-stage>
    <!-- the page of fine lines with a word hidden in it, and the film of slits that reads it, on one canvas -->
    <canvas class="page" data-page role="img" aria-label="A page of fine lines and a dark film of slits: held level over the page, the film shows a hidden word"></canvas>

    <!-- the mark: a small grate of grey lines, and a darker one between them that turns with the film -->
    <header class="mark" data-label>
      <svg class="mark__grate" viewBox="0 0 16 16" aria-hidden="true">
        <g class="mark__back">
          <rect x="0" y="1.0" width="16" height="1"/>
          <rect x="0" y="4.2" width="16" height="1"/>
          <rect x="0" y="7.4" width="16" height="1"/>
          <rect x="0" y="10.6" width="16" height="1"/>
          <rect x="0" y="13.8" width="16" height="1"/>
        </g>
        <g class="mark__film" data-mark-film>
          <rect x="2" y="2.6" width="12" height="1"/>
          <rect x="2" y="5.8" width="12" height="1"/>
          <rect x="2" y="9.0" width="12" height="1"/>
          <rect x="2" y="12.2" width="12" height="1"/>
        </g>
      </svg>
      <span class="mark__name" data-rise>Scrim</span>
    </header>

    <!-- the hint: what to do, and what to do once the film moves -->
    <p class="hint" data-label>
      <span class="hint__roll">
        <span class="hint__line">Move the pointer to slide the film.</span>
        <span class="hint__line">Hold it level, and the word appears.</span>
      </span>
    </p>

    <!-- the reading: how far the film is turned, then in register -->
    <p class="readout" data-label>
      <span class="readout__roll">
        <span class="readout__line" data-tilt>tilt 0.0&deg;</span>
        <span class="readout__line readout__line--on">in register</span>
      </span>
    </p>

    <!-- the hidden word, for screen readers -->
    <p class="sr" data-said></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.

18 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 word under the film” from Animorio and fit it into my site

What it does

A paper-coloured page with, in the middle, a rectangle of thin, close, near-black horizontal lines, all the same distance apart. Inside the letters of a word (Latent) the lines are moved down by half a step: from a distance the page is an even grey and the word barely shows, like a watermark. In one corner sits the made-up brand Scrim, with a small grate by it; at the bottom a line says what to do, and on the right a technical reading says how many degrees the film is turned. On opening the lines are drawn from the middle row up and down, each opening from the centre to both sides; then a black film comes in from the right, turned and slightly lifted, slides to the middle, straightens with one last swing and lies down. The film has thin slits as far apart as the lines: when it is level and in register every slit falls on a line outside the letters and between two lines inside them, so the film looks black and the word lights up in light stripes. At that moment the four corner marks of the film turn red and grow with a small bounce, the reading rolls to “in register” and the word gains weight. Moving the pointer, the film follows it sideways on a spring; the higher or lower the pointer is on the page, the more the film turns (in a band in the middle it stays level). As soon as it is askew, the slits cross the lines and wide light and dark moiré bands run across the film, narrower the more it is turned; the word still reads in places, like a negative. Moving fast, the film leans behind the hand and lifts a hair: the slits spread wider than the lines and the bands run the other way. Back in the middle band the film straightens with a swing, and in the last fraction of a degree it clicks into register as if on a detent. When the pointer leaves, it goes back to the middle and into register again. The small grate of the brand turns with the film too.

How it works

The page with the word inside

On a hidden canvas the script draws the lines: one every PITCH pixels, LINE times the step thick, with step and thickness rounded to the real pixels of the screen (and the step even, so half a step is a whole pixel too). Then it writes the word on a second canvas that works as a mask: with destination-out it removes the lines inside the letters, and on a third canvas, with destination-in, it keeps only inside the letters the same lines moved by half a step. The two halves together are the page, repainted only when the word changes weight.

The film of slits

The film is a single path of black rectangles: between one slit and the next there is a bar as wide as the step minus the line. It is drawn on every frame with translate, rotate and scale around its centre, which always sits a whole number of steps from the first line: level and at its size, every slit falls exactly on a line. Outside the letters the dark line shows, inside them the paper: the word reads light on black.

The springs and the moiré

Three damped springs, worked out in steps of a hundred and twentieth of a second: the position follows the pointer sideways, the rotation follows the height of the pointer (level in the LEVEL band, up to TILT degrees at the edges), the scale follows the speed (LIFT). The moiré is not drawn: it comes by itself from the two grids. Turned by a small angle they make bands about the step divided by the angle wide; with a different scale the bands run across.

The register detent

A quarter of a degree from level the rotation spring gets stiffer and nearly damped, like a detent: the film clicks to zero with one last small swing, and once it is close enough it is set exactly to zero, so the slits sit sharp on the lines. If it stays level and at its size for a moment it is in register: GSAP colours and stretches the corner marks with back.out, rolls the reading and brings the weight of the word from WEIGHT minus 300 to WEIGHT, repainting the page in steps of ten.

The entrance, the rest and the leaving

The entrance is a GSAP timeline: the lines are drawn from the middle row, the film comes in from the right turned and lifted (the timeline moves the targets, the springs add the lag and the settling). When nothing moves any more the canvas is not redrawn. When the pointer leaves, the targets go back to the middle and to zero; with a finger you drag, and lifting it does the same.

Values to change

NameFileDefaultWhat it changes
WORDscript.js'LATENT'The word hidden in the page. Short and in capitals works best: it reads in stripes.
PITCHscript.js6The distance from one line of the page to the next, in pixels. On the phone it gets a little smaller by itself.
LINEscript.js0.36How thick the lines and the slits are, as a share of PITCH: thicker, the word lights up brighter but the page is darker.
TILTscript.js7How many degrees the film turns with the pointer at the top or bottom of the page. More degrees, narrower moiré bands.
LEVELscript.js0.24The band in the middle of the page where the film stays level and in register, as a share of its height.
SWINGscript.js0.35How much the film swings past level before it settles (0 = not at all).
LIFTscript.js0.025How much the film lifts when the hand is quick: it grows a hair larger and the slits spread wider than the lines.
WEIGHTscript.js760The weight of the word when the film is in register, from 300 to 900: out of register it is 300 lighter.

Try them in the Playground

When to use it

At the top of a site that wants a quiet front page with a surprise to look for: a graphic design or print studio, a photography show, a label, a product launch that keeps its name hidden. The hidden word can be a name, a date or an invitation.

On phones

The same animation with the sizes adapted: the page takes nearly the whole width, the lines are a little closer, the word picks by itself the cut of the typeface that makes it largest and the film stays inside the screen with its corner marks. The finger is the pointer: dragging, the film slides and turns, lifting it, it goes back to the middle and into register.

With reduced motion

Nothing slides and nothing turns: the page is already ruled and the film fades in at the middle, already in register, with the word light and the corner marks red. The pointer does not move it.

Pitfalls

  • A soft rotation spring (to get the swing) comes to rest a few hundredths of a degree from level, and on a film 750 pixels wide 0.02 degrees is enough to shift the slits by a quarter of a pixel at the edges: the word came out patchy. Near level the spring becomes a stiff detent and is then set exactly to zero.
  • With the lines of the page as black as the film the moiré was very strong but the page looked like a dark block; with light grey lines the page was light but in the thumbnails the word no longer read (grey on black stripes and paper on black stripes become nearly the same grey from a distance, and the spaces between the letters looked like other letters). A warm near-black grey, at just over a third of the step, keeps the page a touch lighter than the film and the word light even when small.
  • The autopilot and the hand are never fully still: with the scale tied to any speed the film would never go into register. Lifting starts only above 600 pixels a second, and the lean behind the hand above 320.
  • The hidden word does not vanish completely: where the lines move by half a step the vertical edges of the letters make a small step, and up close the word can be guessed. That is how these hidden images really behave; closer lines hide it better.

Libraries

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.

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