Surfaces · effect

ScrollSlideshow

A scroll-run slideshow on a paper web that bows as it accelerates.

scroll the page, then stop and watch it settle flat

Paste into Claude Code, Cursor or any agent. Pinned to a tag, so it will not change under you.

Options

paper #fbfaf4

css colour

Shown wherever the bow has pulled the image away from the edge of the frame. Match it to the page behind, or the gap reads as a border that appears only while scrolling.

bend 0.09

0 to 0.3

How hard the sides lag behind the middle. This is the bow, and it is the option you came for. Past about 0.12 it stops being a press and starts being a fisheye.

slip 0.018

0 to 0.15

How far the whole web slides against the direction of travel, the way anything with mass does when it is pulled. Small: this is the part you feel rather than see.

fringe 0.004

0 to 0.03

Separation between the colour channels at the edges while moving. A press running colour work strikes one plate per ink, and a moving web lands them a fraction apart. Keep it under about 0.01 or it reads as a broken monitor.

grain 0.4

0 to 1

Paper tooth over the image. Fixed per slide rather than per frame, because grain that crawls is a screen artefact and grain that sits still is paper.

reference 1.6

0.2 to 6

The scroll velocity that counts as full speed, in screens per second. Above it the effect stops growing. Lower makes the web bow more readily; too low and an ordinary wheel click maxes it out.

crossfade 0.55

0.05 to 1

Fraction of each slide's travel spent crossing to the next. Low holds each picture still and then cuts; 1 never stops dissolving.

What it needs

Dependencies
None at all
Browser
WEBGL2
Frame budget
~1.2 ms · 1 context
Pinned to
v0.6.2
  • The images are the host element's own <img> children. They are hidden from sight and left in the document, so the alt text and source order are whatever you wrote, and a page with no JavaScript still shows the pictures
  • At rest nothing is distorted. The whole effect is a function of scroll velocity, so a reader who has stopped scrolling is looking at a photograph
  • One WebGL2 context, one full-screen triangle, no buffers and no attributes
  • Textures are CLAMP_TO_EDGE. The bow samples past the edge of the image and a repeating wrap would tile the opposite side of the picture into the gap
  • Two texture reads per channel per pixel while crossfading, three channels, so six taps. It is fill rate and nothing else
  • Images upload once, on load. Nothing is uploaded per frame
  • Scroll velocity is measured on the scroll event and decayed in the frame loop. A per-frame difference reads zero on most frames, because scroll events do not fire every frame, and the effect stutters