
Marquee that speeds up with scroll
Scroll, Text, Hover
Free
Minimal style: five white cards with a hairline edge, squared in a stack, a word in the corner of each. As the pointer comes near, the deck spreads sideways in a row like cards laid on a table, each on its own spring and a moment after the one before, until the words read as a sentence; the card under the pointer lifts a few pixels and takes the red. Far away, the cards gather up and the stack squares itself with a last small tap.
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>Cards laid on the table</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>
<!-- the sentence people read; the cards below are only its picture -->
<h1 class="sr" data-read>Lay your cards on the table.</h1>
<!-- five white cards squared in a stack, one word in the corner of each. As the pointer (or a finger) comes close,
the deck spreads sideways like cards laid on a table, every card on a spring of its own and a moment after
the one before; the card under the pointer lifts a few pixels and takes the accent. Far away, the deck
gathers back into a stack and squares up with a last small tap -->
<div class="table" aria-hidden="true" data-table>
<div class="ruler" data-ruler>
<i class="ruler__line" data-line></i>
</div>
<div class="deck" data-deck></div>
</div>
<!-- the same thing for the keyboard: focus spreads the deck, the arrows move from card to card -->
<div class="handle" role="slider" tabindex="0" aria-label="Cards on the table" aria-valuemin="1" aria-valuemax="5" aria-valuenow="1" data-handle></div>
<p class="note" aria-hidden="true" data-note>
<span class="note__rule"></span>
<span class="note__hint" data-hint>Move closer</span>
<span class="note__rule"></span>
</p>
<!-- an invisible word, to measure how wide the words are -->
<span class="probe" aria-hidden="true" data-probe></span>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>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.
19 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “Cards laid on the table” from Animorio and fit it into my siteThe cards are made by the script, one for every word in WORDS: a div the script moves and, inside it, a face with a white fill, an edge made with a one-pixel inset box-shadow, the word in the top right corner and a thin line under it. The first card lies on top of all the others (highest z-index) and each one leaves the right strip of the card below uncovered: that is where the word sits. The ruler is a one-pixel line stretched with scaleX and one tick per card stretched with scaleY, so they only move with transform.
On every frame the script measures the distance between the pointer and the rectangle the cards cover at that moment, and turns it into a wish from 0 (closed) to 1 (open) with a soft curve: open within a third of REACH, closed beyond REACH. Since the rectangle grows as the deck opens, a stack that starts to open comes closer to the pointer by itself and opens all the way, and one that starts to close closes all the way: no cards left halfway, trembling on the border.
The script keeps the wishes of the last half second with their time. Every card reads the wish from DELAY seconds ago multiplied by its place: opening, the first to move is the whole stack, which slides to the start of the row, then the cards come out from under it from the second to the last; closing, the order is reversed, the last card comes back first and the stack goes back to the middle last. The position of a card is (i × q − half × g) × the strip, where q is how far it has come out and g how far the stack has slid: at rest the row is always centred.
Every card has its own damped spring, a little softer than the one above, chasing its wish; gsap.ticker moves it forward in small steps of 1/240 of a second, so it stays steady even when a frame comes late. BOUNCE lowers the damping: at 0.4 the card goes a couple of pixels too far and comes back. The speed of the spring also narrows the word while it runs (the wdth axis of Archivo, down to 76).
The card under the pointer is the first from the top whose rectangle holds it, without its lift, so it does not slip from under the pointer as it rises. It has a second, livelier spring that lifts it by LIFT pixels, and an accent from 0 to 1 that fades quickly: edge, word, line and tick go from ink to red, and the word goes from 380 to WEIGHT. When the deck is closed again a GSAP tween takes a number from 1 to 0 with elastic.out: the thickness of the stack and the ticks follow it, and the stack squares itself.
| Name | File | Default | What it changes |
|---|---|---|---|
WORDS | script.js | ['Lay', 'your', 'cards', 'on the', 'table.'] | The words: one card per word, from 2 to 7. The first lies on top of the stack. Short words, since each one has to fit its strip. |
REACH | script.js | 200 | How near the pointer has to come, in pixels from the edge of the cards, for the deck to spread (70% on a phone). |
STEP | script.js | 0.74 | How much of every card the next one leaves uncovered, as a share of its width: 0.4 a tight deck, 1 the cards side by side. |
DELAY | script.js | 0.06 | How long every card waits after the one before, in seconds: 0 they all start together. |
BOUNCE | script.js | 0.4 | How far the cards go past their place before they settle: 0 not at all, 1 a small bounce. |
LIFT | script.js | 8 | How many pixels the card under the pointer lifts. |
WEIGHT | script.js | 680 | The weight of the word on the card under the pointer, from 300 to 900; the others weigh 380. |
SPEED | script.js | 1 | The speed of the springs: 2 is twice as fast, 0.5 half as fast. |
For a few words revealed as you come closer: a tagline on a launch page, the services of a studio, the courses of a tasting menu, the days of a festival, the steps of a method. It works with short words and three to seven cards; for long sentences plain text is better.
The same, with smaller cards so the open row fits the width of the screen: a finger touching near the cards spreads them, the card under the finger lifts, and a moment after the finger lifts the deck closes again.
No movement: the row is already laid out in its place and fades in; the card under the pointer does not lift, it only takes the red and the weight with a fade.
GSAP 3.15.0, Archivo (font variabile, assi wdth e wght) 5.3.0.
Compressed with gzip, the three demo files weigh 8.9 KB; the libraries: GSAP 28 KB.
Tested on 16 October 2026
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.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.