
The menu that opens from its button
Menus, Transitions, Text
Free
The stories of a small museum of drawing made of type alone, in the minimal style: every story is one big sentence. Its words come in one after the other while the font’s axes open up, and the story reads itself in its own time, letter by letter, while the thin bar at the top fills: the word in the accent colour takes its colour and its slant only when it is read. Tap on the right to go on, on the left to go back, hold to pause.
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, viewport-fit=cover">
<title>One-sentence stories</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/roboto-flex@5.3.0/full.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the stories of a small museum of drawing, Velum, made of type alone: every story is one sentence. Its words
come in one after the other while the font's width and weight open up, and the story reads itself in its own
time, letter by letter, while the thin bar at the top fills: the word in the accent colour takes its colour
and its slant only when it is read. A tap on the right goes on, on the left back; holding stops the time.
The sentences are written by script.js, from STORIES -->
<section class="stories" aria-label="Stories from Velum" aria-roledescription="stories" data-stories data-autoclic>
<div class="viewer" data-viewer>
<div class="bars" aria-hidden="true" data-bars></div>
<div class="head" data-head>
<span class="head__name">Velum</span>
<span class="head__when" aria-hidden="true">
<span class="head__ago" data-ago></span>
<span class="head__paused" data-paused-word>Paused</span>
</span>
<button class="head__pause" type="button" aria-label="Pause the stories" data-pause>
<svg class="i-pause" viewBox="0 0 24 24" aria-hidden="true"><path d="M8.5 5.5v13M15.5 5.5v13"/></svg>
<svg class="i-play" viewBox="0 0 24 24" aria-hidden="true"><path d="M8 5.8v12.4l10-6.2z"/></svg>
</button>
</div>
<div class="text" aria-hidden="true" data-text>
<div class="measure" data-measure></div>
</div>
<p class="sr" data-now></p>
<button class="tap tap--back" type="button" aria-label="Previous story" data-back>
<svg class="tap__sign" viewBox="0 0 24 24" aria-hidden="true"><path d="M14.5 5.5L8 12l6.5 6.5"/></svg>
</button>
<button class="tap tap--next" type="button" aria-label="Next story" data-next>
<svg class="tap__sign" viewBox="0 0 24 24" aria-hidden="true"><path d="M9.5 5.5L16 12l-6.5 6.5"/></svg>
</button>
<div class="foot" data-foot>
<span class="foot__rule" aria-hidden="true" data-rule></span>
<p class="label" aria-hidden="true" data-label>
<span class="label__what" data-what></span>
<span class="label__when" data-when></span>
</p>
<button class="again" type="button" hidden data-again>
<span class="again__word">Watch again</span>
<svg class="again__sign" viewBox="0 0 24 24" aria-hidden="true"><path d="M18.4 9.2A7 7 0 1 0 19 13.6"/><path d="M19.2 4.8v4.6h-4.6"/></svg>
</button>
</div>
</div>
<p class="sr" aria-live="polite" aria-atomic="true" data-live></p>
</section>
</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.
37 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 “One-sentence stories” from Animorio and fit it into my siteEvery sentence is written first in an invisible twin, as wide as the sentence’s room, at the final axes (wdth WIDTH, wght WEIGHT, the accent word heavier and already slanted) and with text-wrap: balance, so the browser breaks and evens the lines. A bisection finds the biggest size, up to BIGGEST, at which the sentence fits in height and no word runs over. Then every word is made again for real, absolutely positioned where the twin had it: from there the axes can move it without the line moving.
Every word has three custom properties, --wd, --wg and --wa, which GSAP animates and the letters read in font-variation-settings. New words start from a width NARROW never wider than the final one and from a thin weight, rise TRAVEL ems with back.out(1.5), a pixel too far and back, and the weight lands with back.out(1.8): a hair heavy, then it settles. Since a word held at its left only widens towards its final width, it never touches the next one. The old ones rise, narrow and lose weight in power2.in.
The choreography is worked out from the geometry: for every old word it keeps the rectangle it takes on its way out (its place plus its travel) and the instant it will be fully transparent; every new word, in reading order and STAGGER seconds after the one before, waits for the latest of those its way in touches. Going on the words go from the first, going back from the last and downwards.
The story’s time is a GSAP tween of DURATION seconds; it fills the bar and runs the reading head, with an index per letter (spaces count as a breath). Every letter goes with a smoothstep 3.2 letters wide from UNREAD to full ink and from grade -150 to 0: Roboto Flex’s GRAD axis changes the thickness without changing the width, so nothing moves; the letter under the head gets a knock of extra grade. The accent letters go from the ink to the vermilion and from slnt 0 to -10 on the same curve. Kerning and ligatures are off, so letters with different settings do not shift by fractions of a pixel.
The tap zones are two real buttons, Previous story and Next story. A press longer than HOLD seconds does not change story: the time’s speed goes to zero in 0.26 s with power2.out (the tween’s timeScale), so bar and reading brake together, and it starts again with power1.in. Back on the first story the time rewinds in 0.45 s and the reading with it. The time and Paused swap places through a slot: one leaves upwards, the other comes up from below. A click from a script on the viewer (the library’s preview) makes a round: first story, on, back, a long press, on to the end.
| Name | File | Default | What it changes |
|---|---|---|---|
DURATION | script.js | 5 | The seconds each story stays on: the sentence is read in the first four fifths. |
HOLD | script.js | 0.2 | The seconds a press must last to stop the time instead of changing story. |
STAGGER | script.js | 0.05 | The seconds between one word coming in and the next. |
TRAVEL | script.js | 0.3 | How far the words travel coming in and going out, in ems. |
NARROW | script.js | 34 | The font width (25 to 151) the words come in from and go out to; never wider than WIDTH. |
WIDTH | script.js | 60 | The font width of the sentence once it has landed, 25 to 151: narrower, the sentence comes bigger. |
WEIGHT | script.js | 540 | The sentence’s weight, 100 to 1000. |
ACCENT_WEIGHT | script.js | 690 | The weight of the word between asterisks, the one that takes the accent colour. |
UNREAD | script.js | 0.34 | How much ink the letters have before the story reads them, from 0 to 1. |
BIGGEST | script.js | 72 | The biggest a sentence can be, in pixels on a viewer 320 wide. |
STORIES | script.js | [['The north wing *reopens* on Saturday.', 'North wing', 'Saturday, 10 am', '2 h'], …] | The stories: the sentence (the word between asterisks takes the accent), the label, its second half and how long ago. |
LAST | script.js | You are all *caught up*. | The sentence after the last story, over Watch again. |
For the stories of a museum, a gallery, a magazine or a publisher, where the thing to say is one sentence: an opening, a show, an opening time, a new issue. Or as timed announcements at the top of a page, with no images to prepare.
The viewer is 320 pixels wide and as tall as a phone, and on short screens it shrinks keeping its proportions: the sizes inside are in cqw and the sentence is measured again. It is tapped on the right and the left and held with a finger; the long press does not open the system’s menu nor select text. The same animations as on a computer.
The time starts stopped (the button says Play) and the stories do not go on by themselves. The words do not rise nor change axes: the old sentence fades, then the new one fades in word by word, already read, with the accent word already coloured and slanted. The time stops and starts without easing.
GSAP 3.15.0, Roboto Flex (font variabile, con gli assi della larghezza, del peso, del grado e dell’inclinazione) 5.3.0.
Compressed with gzip, the three demo files weigh 12 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.