
Blinds over images
Images, Scroll
NewFree
Scroll inside the preview
A minimal-style scroll animation for images: a photo is printed as a cyanotype as you scroll. The sun runs along a thin arc and a timer counts the minutes, the paper coated in iron salts turns grey-blue where the light gets through the negative, then the negative is lifted away, a rinse runs down the sheet with a wavering wet edge and the photo comes up in Prussian blue, which deepens as it dries.
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>Ferro cyanotype print room</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
<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>
<!-- a made-up cyanotype print room, Ferro. The stage sticks while you scroll: a sheet coated in iron salts lies under a
negative and the sun runs along a thin arc; the paper turns grey-blue where the light gets through, the negative is
lifted away, a rinse runs down the sheet and the photo comes up in Prussian blue, then deepens as it dries. The
photo and the negative are drawn in the script -->
<main>
<section class="corsa" data-corsa>
<div class="palco" data-palco>
<header class="testa">
<a class="marchio" href="#">Ferro</a>
<p class="testa__nota">Cyanotype print room</p>
</header>
<div class="cielo" data-cielo aria-hidden="true">
<svg class="arco" data-arco viewBox="0 0 100 30" preserveAspectRatio="none">
<path class="arco__guida" data-arco-guida d="M2 28 Q50 -10 98 28" />
<path class="arco__corsa" data-arco-corsa d="M2 28 Q50 -10 98 28" />
</svg>
<span class="sole" data-sole><span class="sole__disco"></span></span>
<span class="tempo" data-tempo>0:00</span>
</div>
<div class="foglio" data-foglio>
<canvas class="foglio__tela" data-tela role="img" aria-label="A cyanotype print of a hill with a lone tree, a low sun and a lake, coming up in Prussian blue"></canvas>
</div>
<ul class="passi" data-passi aria-hidden="true">
<li class="passo" data-passo>Expose</li>
<li class="passo" data-passo>Lift</li>
<li class="passo" data-passo>Rinse</li>
<li class="passo" data-passo>Dry</li>
</ul>
<div class="finale" data-finale>
<h1 class="finale__titolo" data-finale-titolo>Twelve minutes of sun.</h1>
<p class="finale__nota" data-finale-nota>Hill and lake, printed in iron salts on cotton paper.</p>
</div>
<p class="cenno" data-cenno><span>Scroll to print</span><svg viewBox="0 0 20 20" aria-hidden="true"><path d="M10 3v13M4.5 10.5 10 16l5.5-5.5"/></svg></p>
</div>
</section>
<footer class="piede">
<p>Ferro prints by hand, in the sun, one sheet at a time. Bring a negative or choose one of ours.</p>
</footer>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.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.
26 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 photo printed by the sun” from Animorio and fit it into my siteThe script draws the landscape in greys on a separate canvas (sky, sun, hills, tree, lake, reflection) and reads its pixels: each grey is how much light the scene has. From that it makes two layers: the negative, dark where the photo is bright, and the print mask, full where the salts lie outside the negative and equal to 1 minus the light under the negative. The mask is coloured twice, in grey-blue (exposed and wet) and in Prussian blue (rinsed).
A ScrollTrigger without a timeline reads the progress, Lenis smooths the scroll and an exponential average chases it (SMOOTH). The number is split into four stretches: the exposure (0.03 to 0.46, power2.inOut), the negative lifting (up to 0.6), the rinse (up to 0.86) and the drying. Every frame the canvas is redrawn: below the edge of the rinse the salts and the grey-blue with an opacity equal to the exposure, above it the Prussian blue.
The edge of the rinse is a line made of two sines running with time, WAVE pixels wide halfway and flat at the start and the end; above it there is a darker veil, the wet, which goes as it dries, and a few drops run ahead of it. The shadow of the negative is its rectangle blurred by 3 pixels, moved away from the sun (SHADOW).
The current step goes to weight 560 and width 96 in Archivo, gets a blue rule growing under it with scaleX and arrives with a small two-pixel elastic; the done ones stay in ink. At the end the title in Newsreader goes from weight 260 to 470 and from optical size 72 to 30. When the rinse ends the sheet gets a kick on a damped spring (SETTLE).
| Name | File | Default | What it changes |
|---|---|---|---|
MINUTES | script.js | 12 | How many minutes of sun the timer counts. |
DENSITY | script.js | 1 | How dark the print comes up: 0.6 is pale, 1.2 deep. |
WAVE | script.js | 4 | How much the wet edge of the rinse wavers, in pixels. |
SHADOW | script.js | 1 | How far the shadow of the negative slides with the sun: 0 keeps it still. |
SETTLE | script.js | 26 | How hard the sheet settles when the rinse is done: 0 keeps it still. |
SMOOTH | script.js | 6 | How quickly the print catches up with the scroll: less is softer. |
RIDE | script.js | 1 | How much scroll the whole thing takes: 2 is twice as long. |
For the page of a photographer, a print room, a darkroom workshop or a photography exhibition: a photo that reveals itself as you scroll while telling how it was printed, ending with a headline under the print.
The same animation with the sizes adapted: the sheet takes almost the whole width and is measured on the height too, the sun’s arc sits above it and the step words below. Everything is worked out again if the window changes size, even halfway.
Nothing moves and the scroll is not smoothed: the print is already rinsed and dry, with the sun at the end of its arc, the timer at 12:00 and the title underneath, and the page fades in.
GSAP 3.15.0, ScrollTrigger 3.15.0, Lenis 1.3.26, Archivo (font variabile) 5.3.0, Newsreader (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 11 KB; the libraries: GSAP + ScrollTrigger 45 KB, Lenis 5.3 KB.
Tested on 23 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.