Skip to content
animorio
IT
Back to the library

The photo printed by the sun

Images, ScrollGSAP, CanvasMediumCompressed weight: Demo 11 KBGSAP + ScrollTrigger 45 KBLenis 5.3 KBFreeDownload the zip

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.

The code

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>

Playground

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

Or ask Claude Code for it

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 site

What it does

A warm white page with the name of a made-up cyanotype print room, Ferro, at the top. In the middle a sheet of cotton paper with a slightly deckled edge and a yellow-green coat of iron salts, with brush marks at the sides; a glass plate with the negative of a landscape lies on it, and above it there is a dotted arc with a small sun and a timer at 0:00. Under the sheet there are four words: Expose, Lift, Rinse, Dry. As you scroll the sun runs along the arc, the travelled part turns blue and the timer reaches 12:00; the paper around the negative and under the clear parts of the plate turns grey-blue, the shadow of the glass slides across the paper away from the sun and a glare crosses the plate. Then the negative is lifted away with a slight turn and disappears. A rinse comes down from the top: its wet edge wavers, has a line of light and a few drops running ahead; above the edge the yellow washes out and the photo is left in Prussian blue, a hill with a lone tree, a low sun, a lake with a broken reflection and three birds. As it dries the blue deepens and the sheet settles with a small knock. Meanwhile the word of the step you are on gains weight, narrows and gets a growing blue rule under it; at the end the words make way for the title ‘Twelve minutes of sun.’, which comes in light and gains weight. With the pointer over the dry print the sheet lifts slightly and its shadow widens; a click makes it give by a pixel.

How it works

The photo drawn as light

The 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).

One number for the whole ride

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 wet edge and the shadow of the glass

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 step words and the title

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).

Values to change

NameFileDefaultWhat it changes
MINUTESscript.js12How many minutes of sun the timer counts.
DENSITYscript.js1How dark the print comes up: 0.6 is pale, 1.2 deep.
WAVEscript.js4How much the wet edge of the rinse wavers, in pixels.
SHADOWscript.js1How far the shadow of the negative slides with the sun: 0 keeps it still.
SETTLEscript.js26How hard the sheet settles when the rinse is done: 0 keeps it still.
SMOOTHscript.js6How quickly the print catches up with the scroll: less is softer.
RIDEscript.js1How much scroll the whole thing takes: 2 is twice as long.

Try them in the Playground

When to use it

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.

On phones

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.

With reduced motion

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.

Pitfalls

  • The negative lifting away left the sheet and was cut by the edge of the canvas: the canvas is a sixth bigger than the sheet on each side, so there is room for the shadow too.
  • The sun’s arc was in a stretched SVG with vector-effect and its dashes grew long: the arc is drawn in pixels, with its viewBox rebuilt on every resize, and the travelled part uses the path’s real length.
  • The print mask is made by reading the scene’s pixels: it has to be rebuilt on resize, together with the screen’s pixel density, or the photo turns grainy.

Libraries

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.

Testing

Tested on 23 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

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.

All animations