
Blinds over images
Images, Scroll
NewFree
Scroll inside the preview
In the minimal style, a photo essay on paper: five photos in a column that, as you scroll, leave a trail like a slit-scan camera. Their last row of pixels stretches into streaks of their own colours, longer the faster you go, and when you stop it draws back in on a spring; the titles widen with the speed and in the corner the shutter speed slows down.
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>Photos stretched by the scroll</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/anybody@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- the photos are drawn on this canvas, under the words: the page only keeps their places -->
<canvas class="film" data-film aria-hidden="true"></canvas>
<header class="top">
<p class="mark">Halden</p>
<p class="shutter" aria-hidden="true"><span class="shutter__k">Shutter</span> <span class="shutter__v" data-shutter>1/500 s</span></p>
</header>
<main class="page">
<section class="intro">
<h1 class="title" data-title>Long light</h1>
<p class="dek">Five photographs from a slow summer. Scroll fast and they leave a trail.</p>
</section>
<section class="roll" data-roll>
<figure class="shot shot--a" data-shot data-scene="dune" data-ratio="0.8">
<div class="shot__slot" data-slot role="img" aria-label="Dune at five"></div>
<figcaption class="shot__cap"><span class="shot__t" data-cap>Dune at five</span><span class="shot__m">Erg Chebbi, June</span></figcaption>
</figure>
<figure class="shot shot--b" data-shot data-scene="lanes" data-ratio="1.5">
<div class="shot__slot" data-slot role="img" aria-label="Lane four"></div>
<figcaption class="shot__cap"><span class="shot__t" data-cap>Lane four</span><span class="shot__m">Lido pool, July</span></figcaption>
</figure>
<figure class="shot shot--c" data-shot data-scene="tulips" data-ratio="0.8">
<div class="shot__slot" data-slot role="img" aria-label="Rows of tulips"></div>
<figcaption class="shot__cap"><span class="shot__t" data-cap>Rows of tulips</span><span class="shot__m">Lisse, April</span></figcaption>
</figure>
<figure class="shot shot--b" data-shot data-scene="umbrellas" data-ratio="1.5">
<div class="shot__slot" data-slot role="img" aria-label="Beach from above"></div>
<figcaption class="shot__cap"><span class="shot__t" data-cap>Beach from above</span><span class="shot__m">Rimini, August</span></figcaption>
</figure>
<figure class="shot shot--a" data-shot data-scene="neon" data-ratio="0.8">
<div class="shot__slot" data-slot role="img" aria-label="Late bar"></div>
<figcaption class="shot__cap"><span class="shot__t" data-cap>Late bar</span><span class="shot__m">Porto, September</span></figcaption>
</figure>
</section>
<p class="end">End of the roll</p>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lenis@1.3.26/dist/lenis.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.
27 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 “Photos stretched by the scroll” from Animorio and fit it into my siteThe photos are not images on the page: the page only keeps their places (empty divs with aspect-ratio) and the captions. A fixed canvas as big as the window sits under the text, and on every frame the script reads with getBoundingClientRect where each place is and draws the photo there. So the trail can leave the frame without being cut.
On every frame GSAP's ticker measures how much scrollY has changed (Lenis makes the scrolling smooth) and divides it by deltaRatio, so the speed is the same at 60 and 120 Hz. The sign says which way the trail goes: scrolling down the photos rise and the trail comes out of the bottom, scrolling up out of the top.
The length of the trail does not follow the speed at once: it is a spring (SPRING) chasing speed × STRETCH, up to MAX_TRAIL of the photo's height. When you stop the spring goes back to zero passing a little beyond: the trail draws back in with a settle instead of vanishing.
The trail is a drawImage of just two rows of the photo's pixels, taken just inside the edge (where the vignette has not darkened them yet), stretched to the length of the trail: every column of the photo becomes a streak of its own colour. Over it, a gradient from transparent to the paper's colour makes it vanish towards the end (FADE).
The photo titles are in Anybody, a variable typeface with a wdth axis from 50 to 150: the same spring as the trail takes the width from 100 to 150. The shutter speed in the corner picks one of eight values, from 1/500 s to 1/4 s, depending on how long the trail is.
| Name | File | Default | What it changes |
|---|---|---|---|
STRETCH | script.js | 16 | How much the trails stretch for the same speed. |
MAX_TRAIL | script.js | 0.6 | The longest a trail can be, as a part of its photo's height. |
SPRING | script.js | [0.16, 0.7] | The trail's spring: how fast it follows the speed and how much of its swing it keeps (more wobbles more). |
FADE | script.js | 0.7 | Where along the trail it starts to fade into the paper (0 = at once, 1 = only at the end). |
STRETCH_TYPE | script.js | true | The photo titles widen with the speed. |
SMOOTH | script.js | true | Smooth scrolling with Lenis. |
For a long photo story: a travel magazine's reportage, a photographer's page with a series, the diary of a summer on a blog, the gallery of a hotel or a beach club. Whoever scrolls slowly sees clean photos; whoever rushes sees them leave a trail.
The same animation with the sizes adapted: the photos sit in a column almost as wide as the screen, the titles under the photos, and the trail follows the finger's scrolling.
No trails and no widening words: the scrolling is the browser's normal one and the photos stay clean; the title fades in.
GSAP 3.15.0, Lenis 1.3.26, Anybody (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 7.3 KB; the libraries: GSAP 28 KB, Lenis 5.3 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.