# 3D Ring Gallery

> A draggable 3D ring carousel where cards stand on a cylinder in real perspective. Flick it hard and it spins with momentum, motion blur, and a velocity-driven tilt before an elastic snap locks the nearest card front and centre.

Canonical: https://gsapvault.com/effects/3d-ring-gallery
Live demo: https://gsapvault.com/demos/3d-ring-gallery/index.html

| Property | Value |
|----------|-------|
| Type | effect |
| Tier | paid |
| Price | £5 |
| Difficulty | advanced |
| Plugins | Draggable, InertiaPlugin |
| Techniques | 3d-transforms, draggable, momentum, carousel, snap, velocity-reactive |
| Uses Lenis | No |

## Overview

A draggable 3D ring carousel built on Draggable and InertiaPlugin. Cards stand on a real CSS cylinder viewed in perspective, and dragging or flicking spins the whole ring with genuine momentum. Under speed the cards catch a velocity-driven motion blur and the ring tilts on its horizontal axis, then as the throw decays an elastic snap locks the nearest card front and centre while its caption and index counter stagger up through a masked reveal.

## Features

- True 3D cylinder built from a single transform-origin trick, so card placement needs no per-frame trigonometry
- Flick-to-spin momentum via InertiaPlugin, with the throw velocity carried straight into the ring's rotation
- Velocity-driven motion blur and ring tilt that build with spin speed and drain away as the ring settles
- Elastic snap that locks the nearest card front and centre after the throw decays, tuned to overshoot once without wobbling
- Depth-aware cards that dim and scale down as they turn away from the viewer, computed from each card's angle
- Masked caption and index counter that stagger in whenever a new card settles at the front
- Touch-safe dragging: vertical swipes on the ring scroll the page, only horizontal gestures spin it
- Keyboard accessible (arrow keys step one card) with a fully static reduced-motion fallback

## Use Cases

- Print shop or poster store collection browsers with a physical, spin-to-browse feel
- Portfolio case-study selectors that make a short list of projects feel expansive
- Product colourway or edition pickers on e-commerce landing pages
- Exhibition, lineup, or programme showcases for galleries and events
- Album or cover-art discography browsers for artists and labels

## How It Works

Every card shares one transform trick: gsap.set gives each a transform-origin pushed radius pixels behind its own plane, so rotating card i by i * (360 / n) degrees places it on a cylinder facing outward, with correct paint order for free. The ring element gets the same deep origin, and spinning it is a single rotationY write.

A hidden proxy element is driven by Draggable with inertia: true; drag deltas (never absolute pointer positions) feed the ring's rotation, which keeps a vertical touch swipe scrolling the page instead of yanking the ring. On each update a cosine of every card's angle versus the front sets per-card dim and scale, while a gsap.ticker eases motion blur and a rotationX tilt toward the live spin velocity.

When the throw decays, the nearest multiple of the card step is tweened in with elastic.out, and the caption and counter rise through overflow-hidden masks with a small stagger. gsap.matchMedia routes reduced-motion users to a static ring with the front card and caption already visible.

## Integration Preview

How this effect integrates into a page. The full documentation (examples, events, programmatic API, customization guide) ships with the download.

### Quick Start

**1. Add to your HTML `<head>`:**

```html
<link rel="stylesheet" href="path/to/style.css">
```

**2. Add before closing `</body>` tag:**

```html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/Draggable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/InertiaPlugin.min.js"></script>
<script src="path/to/script.js"></script>
```

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

```html
<div class="ring-gallery">
  <div class="ring-stage" tabindex="0" role="group" aria-roledescription="carousel"
       aria-label="Gallery. Use the left and right arrow keys to rotate the ring.">
    <div class="ring-tilt">
      <div class="ring">
        <figure class="ring-card" data-title="First Card" data-sub="Subtitle line">
          <div class="ring-card-inner" style="--grad: linear-gradient(160deg, #22d3ee, #0e7490);">
            <span class="ring-card-num" aria-hidden="true">01</span>
            <span class="ring-card-name">First Card</span>
          </div>
        </figure>
        <!-- Repeat .ring-card for each item. 6-10 cards works best;
             a minimum of 3 is required for the cylinder to form. -->
      </div>
    </div>
    <div class="ring-hint" aria-hidden="true">Drag &middot; flick hard</div>
  </div>

  <div class="ring-meta">
    <div class="ring-caption">
      <div class="ring-caption-mask"><span class="ring-caption-title">First Card</span></div>
      <div class="ring-caption-mask"><span class="ring-caption-sub">Subtitle line</span></div>
    </div>
    <div class="ring-counter" aria-hidden="true">
      <span class="ring-counter-mask"><span class="ring-counter-current">01</span></span>
      <span class="ring-counter-sep">/</span>
      <span class="ring-counter-total">08</span>
    </div>
    <p class="sr-only ring-live" aria-live="polite"></p>
  </div>
</div>
```

Pre-fill the caption and counter with the first card's details so the page reads correctly before the script runs.

### Options

All options are data attributes on the `.ring-gallery` wrapper.

| Attribute | Values | Default | Description |
|-----------|--------|---------|-------------|
| `data-drag-speed` | Any number | `0.25` | Degrees of ring rotation per pixel of drag. Higher = faster spin. |
| `data-snap` | `true`, `false` | `true` | Elastic-snap to the nearest card after a throw. `false` leaves the ring wherever momentum stops it. |
| `data-tilt` | Number (degrees) | `7` | Maximum rotationX tilt the ring takes under speed. `0` disables the tilt channel. |
| `data-blur` | Number (px) | `6` | Maximum motion blur cards catch under speed. `0` disables the blur channel. |
| `data-dim` | `0` to `1` | `0.55` | How much cards dim as they face away from the viewer. `0` disables depth dimming. |
| `data-radius` | Number (px) | auto | Explicit cylinder radius. Leave unset to derive it from the card width and count. |

#### Per-card attributes

| Attribute | Description |
|-----------|-------------|
| `data-title` | Caption title shown when this card settles at the front |
| `data-sub` | Caption subtitle line for this card |

#### Card and stage sizing

Card size and stage height are CSS variables on `.ring-gallery`; the script derives the cylinder radius from the card width, so resizing these rescales the whole ring:

```css
.ring-gallery {
  --card-w: 230px;
  --card-h: 310px;
  --stage-h: 430px;
}
```

The stylesheet already reduces these at 640px so the ring fits phone viewports.

### Accessibility

- **Keyboard**: the stage is focusable; left and right arrow keys rotate the ring one card at a time with the same caption reveal
- **Screen readers**: an `aria-live` region announces the settled card ("Solstice Drift, 1 of 8"); the stage carries `role="group"` and `aria-roledescription="carousel"`
- **Reduced motion**: `prefers-reduced-motion: reduce` gets a static ring with the front card and caption fully visible; arrow keys still work with instant jumps and no momentum, blur, or tilt
- **No JavaScript**: the cards render as a flat, scrollable row; nothing on the page is hidden behind a script

### Dependencies

**Required:**
- GSAP 3.12+
- Draggable plugin
- InertiaPlugin

Both plugins are on the public GSAP CDN (formerly Club-only; free since GSAP joined Webflow).

## What You Get

- `index.html`: working demo page
- `assets/script.js`: commented, readable source
- `assets/style.css`: effect styles
- `README.md`: full documentation with examples and framework integration notes
- `LICENSE.txt`: standard license terms
- Lifetime updates: re-download anytime from your library

## Get the Code

This is a premium effect. The standard license costs £5 one-time and covers unlimited personal and commercial projects with no attribution required. The only restrictions: no redistribution of the code itself and no competing effect libraries.

- [Buy 3D Ring Gallery](https://gsapvault.com/effects/3d-ring-gallery)
- [Effects & Templates Vault (£29 one-time, best value): every current and future effect and template](https://gsapvault.com/effects)

## Judge the Code Quality First

These related effects are free with complete source published, written to the same production standard (cleanup functions, reduced-motion support, framework-agnostic):

- [Image Clip Reveal](https://gsapvault.com/effects/image-clip-reveal.md): Images reveal on scroll with an animated clip-path wipe and a Ken Burns settle, the inner image easing from 1.25 scale down to 1 as the mask opens.
- [3D Card Flip Gallery](https://gsapvault.com/effects/3d-card-flip.md): Team/portfolio cards with 3D flip animation. Hover flip for desktop, tap for touch. Includes staggered entrance and scroll-triggered reveals.

---

From [GSAP Vault](https://gsapvault.com): production-ready GSAP animation effects. Full catalog for agents: https://gsapvault.com/llms-full.txt
