Skip to content
animorio
IT
Back to the library

The photo that comes out of the pixels

Images, ScrollGSAP, Canvas, CSS onlyMediumCompressed weight: Demo 7.5 KBGSAP + ScrollTrigger 45 KBLenis 5.3 KBFreeDownload the zip

Scroll inside the preview

A minimal-style scroll and image animation: in a made-up journal of photographs every picture comes up the page made of a few blocks of colour and, as it rises, sharpens in clean steps, 4, 8, 16, 32 blocks and more, until it is sharp; at every step the frame settles. Beside it, a thin counter says how many blocks there are and in the end turns into the caption.

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>The photo that comes out of the pixels</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- a made-up journal of photographs, Coarse. Every picture arrives in big blocks of colour and sharpens in steps
       as it rises up the screen; the small counter beside it says how many blocks there are, and turns into the
       caption when the picture is sharp. The pictures are drawn by the script -->
  <header class="testa">
    <a class="marchio" href="#" data-entra>Coarse</a>
    <p class="testa__nota" data-entra>Photographs, slowly</p>
  </header>

  <main class="pagina">
    <section class="apertura">
      <h1 class="titolo" data-entra>Look closer.</h1>
      <p class="sotto" data-entra>Five pictures that only show themselves as you come near.</p>
    </section>

    <figure class="foto" data-foto="salt" data-shape="1.5">
      <div class="foto__cornice" data-cornice><canvas class="foto__tela" data-tela aria-label="Salt flats with low hills and the sun"></canvas></div>
      <figcaption class="foto__dida"><span class="foto__conto" data-conto></span><span class="foto__nome" data-nome>Salt flats, before seven</span><span class="foto__riga" data-riga></span></figcaption>
    </figure>

    <figure class="foto" data-foto="stairs" data-shape="0.8">
      <div class="foto__cornice" data-cornice><canvas class="foto__tela" data-tela aria-label="A stairwell seen from above"></canvas></div>
      <figcaption class="foto__dida"><span class="foto__conto" data-conto></span><span class="foto__nome" data-nome>The stairwell at noon</span><span class="foto__riga" data-riga></span></figcaption>
    </figure>

    <figure class="foto" data-foto="pears" data-shape="1.25">
      <div class="foto__cornice" data-cornice><canvas class="foto__tela" data-tela aria-label="Three pears on a table"></canvas></div>
      <figcaption class="foto__dida"><span class="foto__conto" data-conto></span><span class="foto__nome" data-nome>Three pears</span><span class="foto__riga" data-riga></span></figcaption>
    </figure>

    <figure class="foto" data-foto="pool" data-shape="1.5">
      <div class="foto__cornice" data-cornice><canvas class="foto__tela" data-tela aria-label="An empty swimming pool lane"></canvas></div>
      <figcaption class="foto__dida"><span class="foto__conto" data-conto></span><span class="foto__nome" data-nome>Lane four, empty</span><span class="foto__riga" data-riga></span></figcaption>
    </figure>

    <figure class="foto" data-foto="barley" data-shape="0.8">
      <div class="foto__cornice" data-cornice><canvas class="foto__tela" data-tela aria-label="Rows of barley running to the horizon"></canvas></div>
      <figcaption class="foto__dida"><span class="foto__conto" data-conto></span><span class="foto__nome" data-nome>Barley rows in August</span><span class="foto__riga" data-riga></span></figcaption>
    </figure>

    <p class="fine">That is all for this autumn.</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/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.

22 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 that comes out of the pixels” from Animorio and fit it into my site

What it does

The page of Coarse, a small made-up journal of photographs, on a warm white: at the top the name, the title ‘Look closer.’, narrow, arriving from below and widening, and a line under it. Scrolling down, five pictures come one below the other, alternating right and left: salt flats with the sun low, a stairwell seen from above, three pears on a table, the empty lane of a pool, a field of barley. Each one comes in from the bottom of the screen made of four blocks of colour across, split by light threads like a mosaic; as it rises it sharpens in clean steps, 8, 16, 32, 64, 128 blocks, and at every step the frame swells a hair and comes back on a spring. Beside the picture a thin counter says how many blocks there are, for example ‘32 × 21’, and each time it moves up a step, fuller and wider. When the picture reaches the top it is sharp: the counter gives way to the caption, which arrives narrow and widens, and a thin vermilion line draws under it. Scrolling back up, the picture goes back to blocks and the caption back to a number.

How it works

The drawn photos

The five pictures are ours: the script draws them on hidden canvases as big as the frame (at the screen's density), with gradients, ellipses and lines, and adds a light grain from a seeded random number generator, so each picture is always the same. When the window changes size they are drawn again at the new size.

The average colour of each block

For a step with bx blocks across, the picture is halved, then halved again, until it gets close to bx by by, always with high imageSmoothingQuality: that way each block really is the average of its patch and not the colour of a random pixel. Then the small one is drawn back on the big canvas with imageSmoothingEnabled set to false, and the blocks stay crisp. Each step is computed once and kept.

Steps tied to the scroll

Each picture has a tween on a number from 0 to 1 with ScrollTrigger and scrub, from when the frame comes in at the bottom to when its top reaches 30 per cent of the screen. The number, times the steps, says which step it is on: it redraws only when the step changes. Lenis softens the scroll and passes its movement on to ScrollTrigger.

The settle and the caption

At every step the frame starts a touch bigger (or smaller, going back) and returns to one with elastic.out, and the counter comes up from below with back.out. The counter's weight and width are two CSS variables that grow with the step. When the picture is sharp the counter empties, the caption arrives with font-stretch from 70 to 88 per cent and the line widens with expo.out; scrolling back up, it all goes back.

Reduced motion

With prefers-reduced-motion there is no Lenis and no steps: the pictures are already sharp, the captions and the lines already in place, and the header fades in.

Values to change

NameFileDefaultWhat it changes
START_BLOCKSscript.js4How many blocks across a picture has when it comes in.
STEPSscript.js6How many pixel steps before it is sharp (each one doubles the blocks).
SHARP_ATscript.js0.3Where the top of a picture has to be to be sharp, as a share of the screen from the top.
SETTLEscript.js0.012How much the frame swells at every step before it settles (0 = still).
GRIDscript.jstrueThe thin light lines between the big blocks, like a mosaic.
GRAINscript.js0.05How much grain the drawings have.
SMOOTHscript.js0.09How soft the scroll is (smaller = softer).

Try them in the Playground

When to use it

For the gallery of a photographer, a magazine or a studio, a project page with a few large images, a long article with photos between the paragraphs: the pictures earn the look of whoever scrolls down.

On phones

The same animation with the pictures full width and the caption below; the steps are the same and the pictures are redrawn at the screen's size.

With reduced motion

No steps and no smoothed scroll: the pictures are already sharp with their captions and the lines under them.

Pitfalls

  • Shrinking a large picture to four blocks in one go gives the colour of a few random pixels, not the average: the blocks flickered from one redraw to the next. Now it halves in steps, the way a mipmap does.
  • The multiplication sign written as a backslash-u escape turned into the real character in the file: it is written with String.fromCharCode(215).

Libraries

GSAP 3.15.0, ScrollTrigger 3.15.0, Lenis 1.3.26, Instrument Sans (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 7.5 KB; the libraries: GSAP + ScrollTrigger 45 KB, Lenis 5.3 KB.

Testing

Tested on 21 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