
The title that rises from a line
Transitions, Text, Images
NewFree
A photographer’s contact sheet, in the minimal style: twelve black and white frames of the same beach, drawn by the script with film grain. A red grease pencil circles one frame in a single loop, the mark wipes away as the photo lifts off the sheet and grows to the middle with its title underneath, then it goes back into place and the pencil picks another. Clicking picks your own.
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 contact sheet and the red pencil</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/newsreader@5.3.0/opsz.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage" data-stage>
<header class="top">
<span class="top__name">Tidemark</span>
<span class="top__note">Contact sheet, low tide</span>
</header>
<!-- the contact sheet of a made-up photographer: twelve black and white frames of the same shore, drawn by the
script. A red grease pencil circles one; the frame lifts off the sheet and grows, its title rises under it,
then it goes back and the pencil chooses another. Clicking a frame chooses it -->
<section class="sheet" aria-label="Contact sheet" data-sheet>
<div class="frames" data-frames></div>
<svg class="pencil" aria-hidden="true" data-pencil><path class="pencil__loop" data-loop d="M0 0"></path></svg>
<p class="caption" aria-live="polite" data-caption>
<span class="caption__mask"><span class="caption__title" data-caption-title></span></span>
<span class="caption__mask"><span class="caption__note" data-caption-note></span></span>
</p>
</section>
<p class="hint" data-hint>Click a frame</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>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.
15 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 “The contact sheet and the red pencil” from Animorio and fit it into my siteEvery frame is a 720 × 480 canvas drawn only once: sky and sea with two linear gradients, the sun with a radial glow clipped at the horizon, the sun’s path made of pale dashes getting wider towards the bottom, the wave ridges, then depending on the scene a pier, the rocks (an uneven shape), a boat, mist, wet sand. Last comes the grain: every pixel gets noise from a seeded generator, so the photo is the same every time, with a touch of warmth and a vignette.
The mark is an SVG path over the whole sheet: an ellipse a little wider than the frame, turned a few degrees, run for 1.14 turns with the radius wobbling slightly and growing towards the end, so the stroke closes further out than where it began, the way a hand does. It draws with stroke-dasharray and stroke-dashoffset going from the whole length (getTotalLength) to zero, and wipes by going negative.
The chosen frame moves with GSAP’s transform (x, y, scale with the origin at the top left) from its place to the big rectangle computed by layout(), with the LIFT curve. The black border sits on the child: as the frame grows its thickness drops in proportion, so enlarged it stays a print’s border and not a thick frame. The others drop to 0.14 opacity.
Every choice is a timeline built on the spot with the measures of that moment: pencil, flight, caption rising out of an overflow hidden window, wait (HOLD), return. At the end it calls the next choice from PICKS. A click stops the chain, puts the sheet back as it was and shows the clicked frame for longer; a resize does the same and starts again with the new measures.
| Name | File | Default | What it changes |
|---|---|---|---|
PICKS | script.js | [4, 9, 1] | The frames the pencil picks by itself, in turn (0 is the first, top left). |
HOLD | script.js | 2.4 | How many seconds a chosen frame stays big. |
PENCIL | script.js | 0.85 | How many seconds the pencil takes to draw its loop. |
LIFT | script.js | 'expo.inOut' | The curve of the frame flying to the middle and back. |
BIG | script.js | 0.72 | How much of the sheet’s width the big frame takes, from 0.5 to 0.95 (all of it on a phone). |
GRAIN | script.js | 14 | How much film grain the photos have, from 0 to 40. |
For a photographer’s portfolio, the archive of a reportage, a wedding or event gallery, the page of a print lab: wherever there are many photos and you want to show one at a time with a gesture from the work table. The drawn photos can be replaced with real images inside the canvases or with img elements.
The same, with adapted measures: three columns and four rows, slightly thinner borders and pencil stroke, and the chosen frame grows to the full width. Touching a frame picks it.
No flights: the pencil’s loop fades in instead of drawing itself, the chosen frame fades out and comes back already big in the middle, the caption fades in, and the return is the same in reverse. When the page opens the sheet fades in.
GSAP 3.15.0, Newsreader (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 7.6 KB; the libraries: GSAP 28 KB.
Tested on 19 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.