ScrollWarpImage

A sheet drawn through its own frame by the scroll, leading in the middle and level again when it stops.

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 warp has pulled the sheet away from the frame. Match it to the page behind, or a border appears out of nowhere as soon as anyone scrolls. `auto` takes it from the page, so the effect adopts whatever it is dropped onto.

bend 0.06

0 to 0.3

How far the middle of the sheet runs ahead of its sides. This is the arc, and it is the option you came for: half a period of a sine across the width, nothing at the edges and everything in the span between them. Keep it and slip together under about inset x (1 - 2 x inset), or a hard flick pushes the bent edge off the frame.

slip 0.015

0 to 0.15

How far the whole sheet slides against the direction of travel, flat across its width, the way anything with mass does when it is pulled. Small: this is the part you feel rather than see, and it is kept apart from the bend so either can be turned off.

fringe 0.005

0 to 0.03

Separation between the colour channels while the sheet is moving. A press 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.

inset 0.1

0 to 0.25

How far the sheet sits in from every edge of the frame, as a share of the frame. The margin is what the bend happens in: at 0 the bent edge runs off the canvas and is chopped square, which reads as clipping rather than as paper. Measured in the frame rather than in the picture, so a photograph of any shape is inset by the same amount on all four sides.

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 sheet bow more readily; too low and an ordinary wheel click maxes it out.

What it needs

Category
Surfaces, an effect
Dependencies
None at all
Browser
WEBGL2
Frame budget
~1 ms · 1 context
Pinned to
v0.13.0
  • The picture is the host element's own <img> child. It is hidden from sight and left in the document, so the alt text is whatever you wrote and a page with no JavaScript still shows it
  • One arc across the width, sin(pi x), so the sides hold back and the middle goes the way the page is going. The sign is the difference between a sheet being drawn through its frame and one sagging in it, and only the first reads as travel
  • The sheet is a rectangle inset from the frame and the drag carries its boundary with its contents, so the edges curve rather than staying square. Nothing is done to the horizontal axis: a sideways bend has no edge to run along and reads as the picture breathing
  • 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
  • Three texture reads per pixel, one per channel, because the colour fringe evaluates the bow three times
  • The image uploads 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