Source Code

<section class="rt-top"><p>Scroll ↓</p></section>
<section class="rt-stage" id="rtStage">
  <div class="rt-ring" id="rtRing">
    <figure class="rt-card" style="--i:0;--g:linear-gradient(160deg,#6366f1,#22d3ee)"></figure>
    <figure class="rt-card" style="--i:1;--g:linear-gradient(160deg,#a855f7,#ec4899)"></figure>
    <figure class="rt-card" style="--i:2;--g:linear-gradient(160deg,#10b981,#84cc16)"></figure>
    <figure class="rt-card" style="--i:3;--g:linear-gradient(160deg,#f59e0b,#ef4444)"></figure>
    <figure class="rt-card" style="--i:4;--g:linear-gradient(160deg,#0ea5e9,#6366f1)"></figure>
    <figure class="rt-card" style="--i:5;--g:linear-gradient(160deg,#f43f5e,#f59e0b)"></figure>
  </div>
</section>
<section class="rt-bottom"><p>The carousel turned with your scroll.</p></section>

Scroll Rotate Gallery — Free GSAP 3D Carousel Scroll Snippet

Scroll Rotate Gallery · Scroll · Plain HTML, CSS & JS · Live preview

What's included

Features

CSS 3D cylinder
rotateY + translateZ arrange the ring.
Parent spin
Rotating the ring turns all cards.
Pinned + scrubbed
Spin tracks the scrollbar.
Momentum
scrub 1 gives the turn weight.
Idle bob
Looping yoyo keeps it alive.
Composed transforms
y and rotateY animate together.
Hidden backfaces
backface-visibility cleans the far side.
GPU 3D
Transforms only, no reflow.

About this UI Snippet

Scroll Rotate Gallery — A 3D Carousel Driven by Scroll

Screenshot of the Scroll Rotate Gallery snippet rendered live

The scroll rotate gallery is a 3D cylinder of cards arranged in a ring that spins as you scroll, like a carousel turning under your control — an eye-catching way to present a set of images or features. This snippet builds it with GSAP and ScrollTrigger (from a CDN) and pure CSS 3D, no carousel library.

Arranging cards in a cylinder

Each card is positioned with CSS: transform: rotateY(i × 60deg) translateZ(230px). The rotateY fans the six cards evenly around a circle (360° ÷ 6 = 60° each), and the translateZ pushes each out from the center by the cylinder's radius — so they form a ring facing outward. The stage provides perspective and the ring uses transform-style: preserve-3d so the depth renders correctly. This is the classic CSS 3D carousel construction, done entirely with two transforms per card.

Scroll spins the ring

A single tween rotates the whole .rt-ring on its y-axis as the section is pinned and scrubbed over end: '+=200%'. Because the cards are children of the ring, rotating the parent turns the entire cylinder — the cards sweep toward and away from the viewer in sequence. scrub: 1 adds a touch of momentum so the spin feels weighty, and the pin holds the stage so the rotation plays over a controlled scroll distance.

Why rotate the parent, not the cards

Spinning the single ring container is far cheaper and simpler than re-rotating six cards every frame, and it keeps their relative arrangement locked — they stay a rigid cylinder while it turns. The cards' own transforms are set once on load and never change; only the parent's rotateY animates.

A subtle idle bob

An independent, infinitely repeating yoyo tween floats the ring up and down a few pixels with a sine ease, so even when you stop scrolling the carousel feels alive rather than frozen. Because it animates y on the same element while the ScrollTrigger animates rotateY, GSAP composes the two transforms without conflict.

Composited 3D

Everything animates transforms (rotateY, y), so the browser composites on the GPU with no reflow, and backface-visibility: hidden hides the reversed back faces of cards facing away. will-change: transform promotes the ring for a smooth spin.

Customizing it

Change the card count (update the 60deg to 360 ÷ count and the rotate distance), the radius (translateZ), the perspective for more or less drama, or swap gradients for real images. Pair it with a coverflow carousel, scroll perspective cards, or a scroll gallery pin.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to work through the 3D cylinder trigonometry by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why each card's static rotateY(i * 60deg) translateZ(230px) transform places it correctly on the ring, or why rotating the parent ring's rotateY is cheaper and more stable than re-rotating six individual cards every scroll frame. The same assistant can help optimize it — asking whether backface-visibility: hidden is actually saving paint work with only six cards, or how the idle yoyo bob and the scroll-driven rotateY tween compose on the same element without one canceling the other. It's also useful for extending the effect: ask it to support a variable card count computed from 360 divided by the count rather than a hardcoded 60 degrees, add click-to-jump navigation to a specific card, or swap the flat gradients for real product photography with correct backface hiding. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "scroll rotate gallery" 3D carousel in plain HTML, CSS, and JavaScript using GSAP with its ScrollTrigger plugin (load both from a CDN) and CSS 3D transforms — no carousel library, no canvas.

Requirements:
- A stage element with CSS perspective set, containing a ring/cylinder container with transform-style: preserve-3d, containing a fixed number of card elements (e.g. six).
- Position each card once at load using a combination of rotateY, where the angle is that card's index times (360 divided by the total card count), and translateZ pushing it outward by a fixed radius distance — so the cards form a ring facing outward, not a flat stack.
- Give every card backface-visibility: hidden so the reversed side of cards facing away from the viewer never becomes visible during rotation.
- Pin the stage and, using a single ScrollTrigger with pin: true and a scrub value greater than 0 (for a slight momentum feel, not scrub: true), rotate only the ring container's own rotateY property (never touch the individual cards' transforms after initial placement) by roughly one full turn or slightly more as the user scrolls through the pinned section.
- Independently of the scroll-driven rotation, add a separate infinitely repeating yoyo tween on the same ring container that gently moves its y position up and down with a sine easing, so the carousel still feels alive when scrolling stops, and confirm both tweens can animate different transform properties on the same element simultaneously without conflict.
- Confirm scrolling back up spins the cylinder in reverse, purely because the rotation tween is scrubbed — no separate reverse-specific code.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Add the GSAP CDNsInclude gsap and ScrollTrigger from the CDN panel.
  2. 2
    Paste HTML, CSS, and JSSix cards form a 3D cylinder.
  3. 3
    Scroll downThe carousel rotates through the section.
  4. 4
    PauseA gentle bob keeps the ring alive.
  5. 5
    Scroll back upThe ring spins backward — it is scrubbed.
  6. 6
    Change the countUpdate the per-card angle and radius.

Real-world uses

Common Use Cases

Showcases
A spinning coverflow carousel.
Galleries
Pair with a scroll gallery pin.
Feature reels
Rotate through feature cards.
Tunnels
Products
Turn a ring of product card shots.
Brand moments
Open with a scroll zoom hero.
Related: Scroll Velocity Motion Blur
See the Scroll Velocity Motion Blur for a related scroll pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Each card uses transform: rotateY(i × 60deg) translateZ(230px). The rotateY fans the six cards evenly around a circle (360 divided by 6 is 60 degrees each) and the translateZ pushes each outward by the cylinder radius, so they face outward in a ring. The stage adds perspective and the ring uses preserve-3d so the depth renders.

Spinning the single ring container turns the whole cylinder at once, which is far cheaper than re-rotating six cards every frame and keeps their arrangement rigid. The cards' own transforms are set once on load and never change; only the parent's rotateY animates with scroll, so the cards sweep past the viewer in order.

An independent repeating yoyo tween animates the ring's y by a few pixels with a sine ease, while the ScrollTrigger animates the ring's rotateY. GSAP composes both transforms on the same element without conflict, so the carousel keeps a gentle float even when you stop scrolling, rather than freezing.

Yes. Everything animates transforms — rotateY and y — so the browser composites on the GPU with no layout reflow, backface-visibility: hidden removes the reversed back faces, and will-change promotes the ring. Because only the parent animates, adding more cards does not add per-frame animation cost.

Render the cards with their --i index, keep the cylinder transforms in CSS, and in a mount effect register ScrollTrigger and create the pinned rotateY tween plus the looping bob, scoped to a ref. Return a cleanup that reverts the GSAP context so the pin and the infinite tween stop on unmount. Update the per-card angle if you change the count.