059

Isometric Scroll Wall

£10 or the whole Vault, £99

A wall of poster tiles laid on an isometric plane, adjacent lanes travelling in opposite directions and wrapping forever as you scroll, with a hard flick banking the whole plane and any tile pulling square to the viewer on a click.

ScrollTrigger Lenis advanced
6 more details
isometricinfinite-loop3d-transformsvelocity-reactivepinningwrap

This demo reads better at your own screen size than in the frame below:

Updated

About this effect

An isometric wall of poster tiles that scrolls forever. Lanes sit on a single tilted plane and adjacent lanes travel in opposite directions, each one wrapping seamlessly so the wall has no start and no end however far you scroll.

Read the full effect overview

Scroll velocity is the second layer. A hard flick banks the entire plane, fans the lanes apart, stretches the tiles along their own axis and pulls a light sweep across the wall, then springs all four back to the locked isometric angle.

Interaction is deliberately two-stage. Hover or focus raises a tile a little way off the plane and nothing more; a click or tap pulls it square to the viewer while the rest of the wall dims. That split is what keeps the wall hoverable: a hover big enough to turn a tile flat also covers the neighbours the pointer is travelling towards.

Isometric Scroll Wall - GSAP animation effect preview

What's included

10 items
  • Infinite counter-scrolling lanes on one isometric plane, wrapped with gsap.utils.wrap so there is no seam and no reset
  • Lanes self-fill: author four or five tiles and the effect measures the frame and clones them to whatever length covers it, rounding to a whole number of tiles so no tile ever lands beside its own clone
  • Velocity reaction on four channels off one reading -- plane bank, lane spread, tile stretch and a light sweep
  • Elastic settle back to the locked isometric angle, with a ticker watchdog that guarantees the release even though scroll events stop firing
  • Two-stage interaction: hover and focus raise a tile off the plane, click and tap pull it square to the viewer with the wall dimmed behind it, Escape puts it back
  • Only the tiles are hit-testable, so every tile in the frame responds -- the flat ancestor boxes of a 3D plane otherwise sit in front of the tiles and swallow most of the pointer events
  • Geometry driven by four custom properties -- tilt, spin, lane length, scroll energy -- so the whole composition can be re-angled without touching a transform
  • Idle drift keeps the wall alive before anyone scrolls, and is one data attribute away from off
  • Viewport-gated ticker and delegated pointer handling, so a wall of a hundred tiles costs one listener pair and nothing at all once scrolled past
  • Reduced motion and no-JS both fall back to the still isometric wall the markup already is, with no tile hidden

Perfect for

5 use cases
  • Product or app landing heroes that need to show many screens at once without a carousel
  • Agency, studio and festival work walls where the point is volume and texture rather than one case study
  • Feature or integration walls on a SaaS page, with the pull standing in for a detail view
  • Template, theme and asset marketplaces showing a deep catalogue above the fold
  • Editorial and event sites where a wall of poster tiles should feel like a printed sheet rather than a grid

How it works

4 sections

The plane carries a single rotateX/rotateZ pair and every tile inherits it, so the isometric look costs one transform rather than one per tile. Each lane is a fixed-length runway: the script takes the frame's projection onto the lane axis, works out how many tiles that needs, clones the authored tiles up to a whole number of sets and wraps each tile's position into a window centred on the lane. Because the window is exactly the cycle length, every slot is filled exactly once and the loop has no seam.

Scroll drives a single position value rather than a scrubbed timeline: a pinned ScrollTrigger writes the target, a frame-rate independent ticker chases it, and an idle drift is added to the same value so the wall glides on after the wheel stops. Alternating lane directions and slight per-lane rate variance are what stop the counter-motion resolving into a pattern.

Scroll speed is clamped, mapped to degrees and eased with a spring, which is what makes the return to the locked angle settle rather than snap. That one number drives the plane's bank, the lanes' spread, their stretch along the lane axis and the opacity of a screen-blended sweep, and the blend layer is only in the document while it is doing something.

The pull applies the inverse of the plane's rotations to a single tile -- rotateZ then rotateX, the reverse of the order the plane applies them -- so it turns square to the viewer, then translates along the plane's normal to push off the wall. Pointer, focus and click all run through one delegated handler pair on the section, and every element in the 3D chain except the tiles is pointer-events: none, because a preserve-3d ancestor's own flat box sits in front of the rotated tiles across most of the frame.

Plugins ScrollTrigger
Difficulty Advanced
Smooth scroll Lenis integration
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this effect's demo, from the latest scan. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Paid effect

Purchase to unlock the code.

Buying Isometric Scroll Wall opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects. Every paid product is tested for desktop and mobile layouts, reduced-motion handling, animation cleanup and keyboard controls wherever there is something to operate.

£10 Standard license, unlimited projects

Browse free effects

Or the whole library: the Vault, £99, one payment.

Documentation

Quick Start

1. Add to your HTML <head>:

Code snippet omitted: it ships with the download.

2. Add before the closing </body> tag:

Code snippet omitted: it ships with the download.

3. Add the effect HTML anywhere in your <body>:

Code snippet omitted: it ships with the download.

Author as few cards per lane as you like. The script measures the frame and clones them until the lane covers it, so four cards per lane is a normal starting point even on a 4K screen.

Using It With Your Own Design

What the effect needs from your markup. Five hooks, and nothing else:

  • [data-isowall] on the section. This is the element the script configures, the one the pinned ScrollTrigger uses, and the one the delegated pointer handlers are bound to.
  • [data-isowall-viewport] on the element that gets pinned and clips the wall. It must be the thing you want to fill the screen; the script reads its size to decide how long the lanes need to be.
  • [data-isowall-plane] on the single element carrying the isometric rotations. The script animates this one transform, not the cards.
  • [data-isowall-lane] on each lane. Lane order in the DOM is what decides direction: lanes alternate, so lane 1 runs one way, lane 2 the other.
  • [data-isowall-card] on each card. Cards must be direct children of a lane, and every card in a lane must be the same height, because that height is the cycle the wrap is built on.

Cards can be any element. <button> is what the demo uses because the pull is a real disclosure control (it holds a card open so it can be read, and Escape closes it); use <article> instead if you would rather the wall were not focusable, and the hover raise still works.

What is only the demo's styling. Everything inside a card is yours: the .isocard__* classes are demo furniture and the script never looks at them. The fonts, the palette, the accent and cyan card variants, the vignette and the cue chip can all go. The script reads data attributes and card geometry, never a decoration class.

CSS the effect depends on. These are the rules a reskin usually breaks:

  • pointer-events: none on the pivot, the plane and the lanes, with pointer-events: auto on the cards. This is the single most important rule here and the least obvious. In a preserve-3d context those ancestors are flat boxes sitting at z = 0 while the cards are rotated away from that plane, so across most of the frame the ancestor's own surface is in front of the card the visitor is pointing at and swallows the event. Measured on this effect before the rule: 22% of sampled points reached a card. After: 80%, which is simply the share of the frame the cards cover. Delete this rule and the wall reads as "some cards hover and some don't".
  • perspective on the viewport, and transform-style: preserve-3d on the pivot, the plane and every lane. Miss one and the wall flattens: the cards stop sorting by depth and the lift stops reading as coming toward you. Keep the perspective long (3000px+) or the composition stops looking isometric and starts looking like a 3D close-up.
  • The pivot is position: absolute with a translate(-50%, -50%), not a centred grid or flex item. The plane is deliberately larger than the frame, and an over-large grid or flex item is aligned to the start rather than centred, which parks the whole wall in one corner.
  • overflow: hidden on the viewport. The wall runs well past the frame on every side by design.
  • .isowall--live .isowall__lane { height: var(--iso-span) } and .isowall--live .isowall__card { position: absolute; top: 50% }. The script adds isowall--live; without it the lane stays a plain flex column, which is exactly what a visitor with JavaScript off should see.
  • Never put opacity on a lane to dim it. Opacity flattens the 3D context under it. Dim the cards individually, as .isowall--focus does.
  • pointer-events: none on the sheen and fade overlays, or nothing is hoverable.

Options

Set on the [data-isowall] section.

Attribute Values Default Description
data-tilt degrees 48 (42 on touch) Tilt away from the viewer. Past ~55 the cards read as floor tiles
data-spin degrees -38 (-30 on touch) Rotation within the plane. Negative sends the lanes up to the right
data-speed any number 1 How far the lanes travel per unit of scroll
data-drift px/sec 26 Idle drift so the wall is alive before anyone scrolls. 0 parks it
data-scroll viewport heights 2.2 (2.6 on touch) Length of the pinned scroll
data-bank true, false true Velocity bank, spread, stretch and light sweep. Always off on coarse pointers
data-lift true, false true Card raise on hover/focus and pull on click/tap
data-raise px 34 How far a hovered card rises off the plane. Keep it small, or the raise covers the neighbours

CSS Custom Properties

Property Set by Description
--iso-card-w / --iso-card-h you Card size. The height plus the lane's gap is the cycle the wrap uses
--iso-gap you Gap between cards and between lanes
--iso-x / --iso-z script (from data-tilt / data-spin) Plane rotations, with the CSS values as the no-JS fallback
--iso-span script Lane length, measured from the frame
--iso-vel script 0-1 scroll energy. Drive your own reactions off it

Accessibility

  • Reduced motion: the wall stops moving entirely and stays the still isometric composition the markup already is. Nothing is hidden and nothing needs restoring.
  • No JavaScript: the lanes stay plain flex columns and every authored card is visible. The wall is a static design, not a blank frame.
  • Keyboard: focus raises a card exactly as hover does, Enter or Space pulls it square, Escape puts it back and returns focus to it, and :focus-visible carries a real outline. Only authored cards are focusable; the fill clones are aria-hidden and tabindex="-1", so a hundred-card wall is only as many tab stops as you wrote.
  • Touch: tap pulls a card square and holds it there, with aria-pressed tracking the state; tapping it again or tapping the background puts it back. The velocity bank is off on coarse pointers by design.

Performance

  • One transform write per lane per frame plus one per card, all through gsap.quickSetter, with no layout reads in the loop.
  • The ticker is gated on a viewport ScrollTrigger: scrolled past, the wall costs nothing.
  • The screen-blended sweep is only in the document while the wall is actually moving fast, rather than sitting composited at zero opacity.
  • Pointer and focus handling is delegated to the section, so card count does not change the listener count.

Dependencies

Required:

  • GSAP 3.12+
  • ScrollTrigger plugin

Optional:

  • Lenis (smooth scroll integration; the wall glides noticeably better with it)

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.