
The gallery that opens
Galleries, Transitions, Images, Physics
NewFree
A big RAW file loading in a photo app: the picture starts as a single tile of its average colour, then the tile with the most detail splits into four, again and again, each with a small elastic snap, until the landscape is sharp. Once every byte is in, the grid closes, the focus corners lock onto the picture and the real photo comes into focus under the last tiles.
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, viewport-fit=cover">
<title>The dividing photo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/standard.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- a photo app opening a big RAW file: the picture starts as a single tile of its average colour, then the tile
with the most detail left in it splits into four, again and again, each split with a small elastic snap, until
the photo is sharp. The landscape is painted by script.js on a hidden canvas: no image is loaded -->
<section class="raw" aria-labelledby="raw-title" data-raw>
<div class="raw__head">
<div class="raw__words">
<h2 class="raw__title" id="raw-title"><span class="raw__swap" data-title>DSC04182.ARW</span></h2>
<p class="raw__meta"><span class="raw__swap" data-meta>Sony RAW · 14-bit · 62.4 MB</span></p>
</div>
<p class="raw__figure" aria-hidden="true">
<span class="raw__pct" data-pct>61 MP</span>
<svg class="raw__tick" viewBox="0 0 24 24" data-tick><path d="M5.5 12.6l4.1 4.1L18.6 7.4" pathLength="1"/></svg>
</p>
</div>
<div class="raw__frame" role="progressbar" aria-labelledby="raw-title" aria-valuemin="0" aria-valuemax="100"
aria-valuenow="0" aria-valuetext="Not opened" data-bar>
<canvas class="raw__paint" aria-hidden="true" data-paint></canvas>
<!-- the four corners of the focus frame: they lock onto the picture when it is sharp -->
<div class="raw__af" aria-hidden="true" data-af>
<svg viewBox="0 0 16 16" class="raw__corner raw__corner--tl"><path d="M1 15V5a4 4 0 0 1 4-4h10"/></svg>
<svg viewBox="0 0 16 16" class="raw__corner raw__corner--tr"><path d="M1 15V5a4 4 0 0 1 4-4h10"/></svg>
<svg viewBox="0 0 16 16" class="raw__corner raw__corner--bl"><path d="M1 15V5a4 4 0 0 1 4-4h10"/></svg>
<svg viewBox="0 0 16 16" class="raw__corner raw__corner--br"><path d="M1 15V5a4 4 0 0 1 4-4h10"/></svg>
</div>
</div>
<div class="raw__foot">
<p class="raw__info"><span class="raw__swap" data-info>Shot 14 Sep · 35 mm · f/8</span></p>
<button class="raw__btn" type="button" data-action data-autoclic><span class="raw__label" data-label>Open</span></button>
</div>
<p class="sr" role="status" aria-live="polite" data-live></p>
</section>
</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.
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 dividing photo” from Animorio and fit it into my siteThe lake at dusk is painted by the script with the 2D canvas: a gradient for the sky, a radial glow and the sun’s disc, thin clouds as softened ellipses, two ridges of hills made from a 1D noise in four octaves, the near hill with its cypresses drawn as Bézier curves, the water with the sky upside down and the hills mirrored, the road of light as strokes that get wider and sparser towards the shore, the reeds and a vignette. Everything is in fractions of the width and the height and the chance comes from a seeded generator, so the same scene paints at any size: a sharp copy at the screen’s density, with grain, and a small clean copy of 384 × 256 for the analysis.
From the small copy the script builds four summed area tables: red, green, blue and the sum of their squares. The sum over any rectangle costs four lookups, so a tile’s mean colour is S ÷ n and its error, the sum of the squared distances from the mean colour, is Σq − (R² + G² + B²) ÷ n, without ever going over the pixels again. 384 × 256 halves cleanly down to tiles of 3 × 2.
The quadtree is built in full ahead of time, once: a binary heap keeps the leaves in order of error, and every step takes out the one with the most detail and splits it into four, which go into the heap. The order of the splits is the load: the bytes that have arrived say how many splits to show (on a curve a little slower at first, so that the big tiles can be seen). A node is a leaf at step k if it was born before k and splits at k or later: going forward and back costs only a counter.
Every node remembers when it appeared. For 0.62 seconds the four children start from the parent’s centre, at 64% of their size and in its colour, and go to their places on a hand written spring, 1 − 2^(−9t) cos(2.7πt), which overshoots by about a tenth and settles; the colour goes from the parent’s to their own in 0.3 seconds. Underneath, for a moment, the parent stays drawn in its colour, so the background does not show between the children. Leaves at rest are drawn aligned to the screen’s pixels, so without the grid they meet exactly; the grid thins out on the small tiles and the corners are rounded only on the big ones.
When every byte has arrived a GSAP timeline takes the grid to zero, closes the focus corners in with an elastic.out as they turn amber, and raises the sharp copy over the tiles; the canvas takes a small breath of scale. From then on, while nothing moves, the canvas is not redrawn. Open again and Cancel do the opposite: the photo goes down, the grid comes back and a tween takes the split counter back, so the tiles fold up in reverse order, each with a little swell.
The frame is a role=progressbar with aria-valuenow and an aria-valuetext in megabytes, the panel has aria-busy while it loads and an aria-live line says when the file opens, stops on an error, is cancelled and is ready. The button changes its word (Open, Cancel, Retry, Open again) and the pill takes the new width with a bounce; a click from a script, the library preview’s, never cancels.
| Name | File | Default | What it changes |
|---|---|---|---|
FILE_NAME | script.js | 'DSC04182.ARW' | The file being opened. |
MEGAPIXELS | script.js | 61 | How big the photo is, in megapixels (the size in pixels is worked out from it). |
SIZE_MB | script.js | 62.4 | The size of the file, in megabytes. |
SECONDS | script.js | 6.5 | How long a load takes in the demo, in seconds (a real one follows its bytes). |
TILES | script.js | 5200 | How many tiles the photo ends up in before it comes into focus. |
POP | script.js | 1 | The elastic snap of every split: 0 none, 1 lively. |
GRID | script.js | 0.8 | The dark line between the tiles while loading, in pixels. |
FAIL_EVERY | script.js | 2 | In the demo, one load in how many loses the memory card (0 never). |
FAIL_AT | script.js | 0.62 | How far the load that goes wrong gets, from 0 to 1. |
SUN | script.js | 0.68 | Where the sun sets, from 0 (left) to 1 (right). |
SEED | script.js | 7 | The shape of the hills, the trees and the ripples. |
For opening a big picture that arrives in pieces: a RAW from a card, a photo from a network archive, a map, a scan, a generated preview. The quadtree says «the detail is on its way» better than a bar, and after a few bytes you can already tell what the photo is; for a file that is not a picture another indicator is better.
The panel takes the screen’s width less 32 pixels (358 on a 390 screen) and the frame stays 3:2; the canvas paints at the screen’s real density, up to twice, and the sharp photo is repainted at the new size when the screen turns. The button is a pill 34 tall: under a finger it squashes and springs back, and its words widen on the width axis only under the mouse.
No snaps or movement: the children appear in their places at once and fade from the parent’s colour to their own, the frame does not shake, the focus corners light up and go out without closing in, the photo fades up without the breath, and the tiles go back to one at once. The photo still divides, because it is what says how far along things are.
GSAP 3.15.0, Instrument Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 13 KB; the libraries: GSAP 28 KB.
Tested on 8 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.