3D Marquee — Free HTML CSS JS Perspective Gallery Snippet

3D Marquee · Animations · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Perspective tilt
rotateX + rotateZ lay the grid into space.
Alternating columns
data-dir scrolls neighbors opposite ways.
Seamless loop
Cloned tile sets and a 50% translate.
cloneNode duplication
Guarantees the two halves match exactly.
Themeable tiles
Random gradients, swappable for images.
Compositor-driven
Pure CSS keyframes, smooth on mobile.
Readable overlay
A radial scrim protects the headline.
Auto-filled columns
JavaScript populates every column.

About this UI Snippet

3D Marquee — Tilted Perspective Wall of Scrolling Tiles

Screenshot of the 3D Marquee snippet rendered live

The 3D marquee is the dramatic hero backdrop where a grid of image tiles is tilted back in perspective like a wall receding into the distance, with its columns endlessly scrolling in alternating directions — a living gallery behind your headline. This snippet builds it with plain HTML, CSS 3D transforms, and a little vanilla JavaScript to populate and loop the tiles.

Tilting the grid into perspective

The scene is a four-column CSS grid that's transformed with rotateX(52deg) rotateZ(-42deg). That combination lays the wall back and rotates it diagonally, so the flat grid reads as a large surface angling away into space — the signature isometric-ish look. It's sized larger than the viewport (1100×1100) and centered with translate(-50%, -50%) so the tilted plane covers the whole hero with no visible edges. No perspective property is needed here because the rotation alone, at this scale, produces the receding-wall illusion.

Columns that scroll in opposite directions

Each column animates vertically, but adjacent columns move opposite ways: columns marked data-dir="1" run the tmUp keyframe (translating to -50%), while data-dir="-1" columns run tmDown (from -50% back to 0). The counter-motion is what gives the wall its dynamic, woven feel — a single direction would look like one flat conveyor. Both keyframes are pure CSS, so the browser's compositor drives the scroll efficiently.

Seamless looping by duplication

To loop endlessly, each column is filled with a set of tiles and then an exact clone of that set, so the column is two identical halves. Animating by 50% (one half's height) means that when a column reaches the end of the first set, the cloned second set sits precisely where the first began — so the keyframe restart is invisible. The tiles are built in JavaScript and cloned with cloneNode(true), which is both concise and guarantees the two halves match exactly.

Image-free, themeable tiles

Each tile is a 4:3 rounded rectangle filled with a random gradient from a palette, with a soft drop shadow so the wall has depth. Using gradients keeps the snippet dependency-free and the wall colorful; swap the --g background for real image URLs to turn it into a portfolio or product wall without changing the layout or loop logic.

Keeping the headline readable

A radial .tm-overlay darkens the scene from a translucent center to near-opaque edges, so the bright tiles don't fight the text. The content sits above at a higher z-index with a soft text-shadow, so the headline stays crisp and centered over the busy, moving backdrop.

Customizing it

Change the rotateX/rotateZ angles to steepen or flatten the wall, adjust the animation duration for a faster or calmer drift, add or remove columns (each picks up its own direction from data-dir), or swap gradients for images. Tune the overlay stops to reveal more or less of the gallery. Pair it with a hero parallax grid elsewhere or a text generate headline for a bold landing page.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA tilted wall of colorful tiles fills the hero behind a headline.
  2. 2
    Watch the motionColumns scroll up and down in alternating directions, endlessly.
  3. 3
    Note the perspectiveThe grid angles away like a receding surface.
  4. 4
    Read the headlineA radial overlay keeps the centered text legible.
  5. 5
    Swap in imagesSet each tile's --g to a real image URL.
  6. 6
    Adjust the tiltChange the rotateX and rotateZ angles.

Real-world uses

Common Use Cases

Portfolio walls
Show work behind a portfolio hero.
Agency landing pages
Pair with a hero parallax grid section.
Product galleries
A moving backdrop for a text generate headline.
Event microsites
Set a vivid stage near a lamp header.
Photography sites
Tile real shots into a receding wall.
3D transform demos
A reference for tilted scrolling grids.

Got questions?

Frequently Asked Questions

The four-column grid is transformed with rotateX(52deg) and rotateZ(-42deg), which lays it back and rotates it diagonally so the flat grid reads as a surface receding into space. It's sized larger than the viewport and centered so the tilted plane covers the whole hero with no visible edges.

Each column is filled with a set of tiles and an exact clone of that set, making it two identical halves. The columns animate by 50% — one half's height — so when the first set scrolls away, the cloned set sits exactly where it began. The keyframe restart lands on an identical frame, so the loop is seamless.

Columns marked data-dir 1 run an upward keyframe and those marked -1 run a downward one. The counter-motion gives the wall a woven, dynamic feel; if every column scrolled the same way it would look like a single flat conveyor belt. Both keyframes are pure CSS so the compositor handles them efficiently.

Yes. Each tile is a 4:3 rounded box whose background comes from a --g custom property, set to a random gradient by default. Replace --g with a url() image (or swap the div for an img), and the perspective tilt, alternating scroll, and seamless loop all keep working unchanged.

Render the columns and generate the doubled tile lists from a data array in render. The tilt, alternating keyframes, and overlay are pure CSS. If you swap to images, map your URLs into the tiles. In Tailwind, build the grid with grid utilities, apply the rotateX/rotateZ via arbitrary transform values, and define the scroll keyframes in the config.