Play Pause
A column of posters travelling slowly upward between the words PLAY and PAUSE. Each one is rolled into a tube; as it reaches the middle it unrolls itself, holds long enough to be read, and rolls back. Hover any of them and the column stops for you. The whole ticker is one scene, so the poster count costs geometry rather than browser resources.
Included with membership
Members install Play Pause with one command and get the full source, the usage code and the Framer remix, licensed for client work. One membership covers the whole library and the CLI.
When to use it
A showreel that should feel like it is running whether or not anyone is watching — a studio index, a case-study list, a film or fashion landing page. It takes a full screen and reads vertically, so it wants a section of its own rather than a slot in a grid.
- Define posters outside the component or memoise it. A new array identity rebuilds the scene and re-uploads every texture.
- Vary rollRadius and twists a little between items. Identical rolls read as a repeat; small differences make it look like a stack of real posters.
- Keep the images roughly portrait. Each poster is 300 tall and as wide as its own aspect makes it — nothing is cropped, so a landscape image simply comes out wide.
- Give it real height. The column is the height of the component, and a poster unrolls when it reaches the middle of it.
- Keep the set between four and twelve. Fewer and the loop is obvious; more and you are paying for geometry nobody scrolls to.
Avoid:
- Do not expect the posters to unroll on their own under reduced motion. The travel is what brings a poster to the middle, and that is what stops — hover still works.
- Do not use a very wide source. Poster width follows the image's aspect and the scene is about 420 wide, so a panorama runs past the edge and is clipped there.
- Do not put another full-screen WebGL component directly above or below it. Each one holds a drawing context, and browsers only allow a handful at once.
- Do not expect the two words to stay put. They part to clear each poster as it opens, so their resting position is a starting point rather than a layout you can measure against.
Props
A column of posters travelling slowly upward, with the words PLAY and PAUSE set enormous behind them. Each poster is rolled into a tube; as it reaches the middle of the column it unrolls itself, holds long enough to be read, and rolls back up before moving on. The two words part just far enough to clear whichever poster is open, so a wide image pushes them further than a narrow one. Hover any of them and the column stops for you. Colour arrives with the unroll — rolled posters are greyscale.
<PlayPause />
The full arrangement — the travelling column, the typography behind it, and the hover and auto-unroll behaviour. Paints its own background and fills its container, with a 440px floor.
| Prop | Type | Default | Description |
|---|---|---|---|
posters* | PlayPauseItem[] | — | The posters, in order. The list is doubled internally so the loop has no seam — you supply each one once. |
playText | string | "PLAY" | The word on the left. It slides outward and takes the accent colour while a poster is open. |
pauseText | string | "PAUSE" | The word on the right. |
accentColor | string | "#F5FD2A" | Colour the two words take while a poster is unrolled. They return to white when it closes. |
tickerDuration | number | 25 | Seconds for the column to travel one full set of posters. Longer is slower; the loop is seamless either way. |
textGap | number | 32 | Pixels of clearance left between the words and the poster that is opening. The two words part by however much that poster needs, plus this — so a wide image pushes them further than a narrow one. |
className | string | "" | Applied to the wrapper. |
mount | "auto" | "eager" | "eager" | When to build the scene. The default builds it on mount. Set "auto" to wait until the section is near the viewport and the page has frame headroom. The whole strip is drawn in one scene rather than one per poster, so its cost does not grow with the number of items — but it is still a 3D scene, and deferring it below the fold is free. Neither setting takes it off the shared frame loop or overrides the reduced-motion preference. A scene running its own loop would be invisible to the scheduler, so it could not be set aside under load, and it would slow every other animation on the page rather than only itself. |
disableAdaptiveQuality | boolean | false | Keeps the pixel ratio at whatever the device reports, up to 2. By default the runtime caps it on lower tiers. This is not the same as dpr, which pins a fixed value: this one still follows the device, it just stops the runtime throttling it. |
<PlayPauseItem />
typeOne poster. The three roll values are what stop the column looking mechanical.
| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | — | Stable key for the poster. |
src* | string | — | Image URL. Every poster is 300 tall and takes whatever width its own aspect gives it, so nothing is cropped — a landscape source simply comes out wide. |
rollRadius* | number | — | How thick the tube is when rolled. Around 0.5 to 0.65 reads as poster stock. |
twists* | number | — | How many turns the roll makes. About 1 is a single loose curl; higher winds it tighter. |
radiusFalloff* | number | — | How much the roll narrows toward its centre. 0 is an even tube; 0.15 tapers it noticeably. |
<PlayPausePoster />
A single poster on its own, without the column or the typography. It unrolls on hover and fills whatever box you give it. Useful as a standalone card; it carries its own canvas, so use a handful at most on one page.
| Prop | Type | Default | Description |
|---|---|---|---|
src* | string | — | Image URL. |
fit | "cover" | "contain" | "fill" | "fill" | How the image sits in its box. |
planeWidth | number | 3.5 | Width in scene units, used when fit is "fill". |
rollRadius | number | 0.5 | Thickness of the roll. |
twists | number | 2.0 | Turns in the roll. |
radiusFalloff | number | 0.8 | How much the roll narrows toward its centre. |
animationSpeed | number | 3.0 | How quickly it unrolls toward the target. |
hoverProgress | number | 1.0 | How far it unrolls when hovered. 1 is fully flat. |
restProgress | number | 0.0 | How far it stays unrolled at rest. 0 is fully rolled. |
initialProgress | number | 0.0 | Unroll amount when hover interaction is off. |
enableHoverInteraction | boolean | true | Whether hovering unrolls it. Off, it holds initialProgress. |
enableGrayscale | boolean | true | Whether colour fades in as it unrolls. |
backSideDarkness | number | 0.3 | How dark the reverse of the sheet is where the roll shows it. |
floatStrength | number | 1.0 | How much it drifts at rest. Held still under reduced motion. |
widthSegments | number | 64 | Mesh resolution along the roll. Too few and the curl facets. |
heightSegments | number | 32 | Mesh resolution across the sheet. |
getProgress | () => number | — | Reads the unroll amount once per frame instead of taking it as a prop, so a parent can animate it without a React render per frame. Used when hover interaction is off. |
antialias | boolean | true | Edge smoothing on its canvas. |
dpr | number | [number, number] | set from the device tier | Pixel-density cap. |
className | string | "" | Applied to the wrapper. |
style | React.CSSProperties | {} | Inline styles on the wrapper. |
mount | "auto" | "eager" | "eager" | When to build the poster's scene. The default builds it on mount. Set "auto" to wait until the poster is near the viewport and the page has frame headroom. This is a standalone component with its own gate — PlayPause does not render it and does not pass anything down, so the two settings are independent. |
disableAdaptiveQuality | boolean | false | Ignores the device quality tier and renders at full detail. By default the runtime caps the pixel ratio on slower hardware and under frame pressure. It does not turn off reduced-motion handling or take the poster off the shared frame loop. |
ssr: false if your route renders on the server.