
Blinds over images
Images, Scroll
NewFree
In the minimal style: a thin red line draws itself down the middle of the page and opens like a slit. The photo comes out in columns, the middle one first and then the others in pairs towards the sides, each on a spring that goes one pixel too far and comes back; when the last one lands, the caption takes its weight letter by letter.
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 from the slit</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage" data-stage>
<p class="mark" data-mark>Lumen</p>
<!-- the plate: the script places it, cuts the photo into columns and opens them from one slit in the middle -->
<figure class="plate" data-plate role="img">
<div class="cols" data-cols aria-hidden="true"></div>
<i class="edge" data-edge aria-hidden="true"></i>
<i class="edge" data-edge aria-hidden="true"></i>
<figcaption class="cap" aria-hidden="true">
<span class="cap__title" data-title></span>
<span class="cap__meta" data-meta></span>
</figcaption>
</figure>
</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.
13 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 from the slit” from Animorio and fit it into my siteThe photo is split into COLUMNS columns, always an odd number so that one sits on the slit. Every column is a div with its own window (clip-path: inset()) and inside it a child with the whole photo as its background, moved left by the column's distance from the edge: so the open columns meet to the pixel. The column edges sit on whole pixels, and the photo inside is a little wider (MARGIN on each side), so dragging it never shows the paper.
Every column has two numbers, l and r, the edges of its window in pixels from its left side. Closed, the window sits on the edge that faces the slit (the middle one is a line on its centre). Opening, the outer edge runs with power3.out to OVERSHOOT pixels past its place, then comes back with sine.inOut: inset() takes negative values, so the window really goes one pixel outside the column and shows the actual photo, not a gap.
The middle column goes first; then the others, in pairs, STAGGER seconds apart. Each one starts from its neighbour's finishing edge before the neighbour is done, and for a moment a thread of paper stays between them: the photo arrives in strips. Meanwhile the photo inside the column starts DRAG pixels towards the slit and settles with expo.out, and when a column lands the one further in moves one pixel outwards and comes back with an elastic.
The red lines are two one-pixel elements. On every frame the GSAP ticker looks among the open columns for the leftmost and the rightmost edge and puts the lines just outside them, so they never cover the photo. When the windows are closed the two lines coincide and are the slit; at the start they draw themselves with scaleY from the centre, and at the end they close the same way.
The caption is a row of separate letters in Archivo, a variable typeface with the wght and wdth axes. Every letter has two CSS variables, --g and --w, read by font-variation-settings. While the photo opens they sit at 220 and 112 (thin and wide, in grey); when the last column lands GSAP takes --g to 640 with elastic.out and --w to 100, one letter after another, and the colour to black.
| Name | File | Default | What it changes |
|---|---|---|---|
IMAGE | script.js | '' | The photo: an address, or a file loaded in the Playground. Empty, the script draws the photo. The frame takes the photo's proportions. |
CAPTION | script.js | 'Stairs at noon' | The short caption under the photo: it takes its weight when the last column lands. |
META | script.js | 'f/11, 1/250 s' | The small line on the right of the caption. |
COLUMNS | script.js | 9 | How many columns the photo opens in (an even number gets one more, so that one sits on the slit). |
STAGGER | script.js | 0.17 | The seconds between one pair of columns and the next. |
OVERSHOOT | script.js | 1 | How many pixels every column goes past its place before coming back (0 = it stops dead). |
HOLD | script.js | 3 | How many seconds the whole photo stays before going back into the slit. |
LOOP | script.js | true | Whether the photo closes and opens again and again. With false it opens once and stays (a click opens it again). |
To present a single photo properly: the opening of a photographer's or an architect's portfolio, the cover of a photo story, an exhibition page, the photo of a hotel room or a dish on a carefully made page. It works with any photo, including one loaded in the Playground.
The same animation with the sizes adapted: the photo is almost as wide as the screen, the columns are narrower and the caption smaller. A tap on the still photo closes it and opens it again.
Nothing moves: the whole photo and the caption, already at its weight, fade in and stay, with no slit, columns or springs.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 8.1 KB; the libraries: GSAP 28 KB.
Tested on 15 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.