Gallery✨ Premium

Cylinder Marquee 3D

A CSS 3D drum built from your list: it spins on its own, takes a drag with inertia and stops on hover, on focus or with Pause.

Marquee3D CSSDragpreserve-3d

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Gallery has 44 effects, including 6 free. Explore the category →

3 usage examples

Languages spoken on a tour guide's profile — Cylinder Marquee 3D example 1

① Languages spoken on a tour guide's profile

WhenOn a guide's public profile, below the list of tours, a dark “Languages spoken” card holds the drum: French, English, Spanish, German, Italian, Portuguese, Dutch, Polish, each written in its own language. Visitors spin it with a drag.
WhyEight languages fit in one card without a long list; each visitor looks for theirs with a gesture. The text remains a real list, read by screen readers.
Settingsdata-cmq-speed="40", data-cmq-face-w="92", data-cmq-face-h="58"; scene as tall as the card, 260 × 120 px glow, 14 px text. For the capture, the scene sends a demo drag (pointer events) to the real code every 3 s.
Pieces of a ceramics collection at the centre of a studio's hero — Cylinder Marquee 3D example 2

② Pieces of a ceramics collection at the centre of a studio's hero

WhenAt the top of a ceramics studio's page: the “Winter collection” title, then the drum in the middle with six pieces (type in bold, glaze colour below), flanked by “14 pieces” and “from €48”, above the Shop and Visit the studio buttons.
WhyThe drum turns by itself and shows the whole collection in a small space, like a rotating shop window; it stops as soon as the mouse rests on it so a piece can be read.
Settingsdata-cmq-speed="36", 104 × 62 px faces; each <li> holds a <b> (type of piece) and the colour; two-line label (white-space: normal); pink tints (#fb7185) on borders and glow; transparent scene background, reflection and vignette hidden.
Steps of a running 3D render, in an online tool's top bar — Cylinder Marquee 3D example 3

③ Steps of a running 3D render, in an online tool's top bar

WhenIn the top bar of a rendering app, next to the file name: a “Rendering” pill with a small drum (geometry, shadows, textures, lighting, denoise, export) while the preview is being computed.
WhyAt a small size, the drum works as an activity indicator that also says what the job is doing; it takes less room than a list and stays readable at the front.
Settingsdata-cmq-speed="60", 84 × 28 px faces, perspective reduced to 420 px; 186 × 52 px scene without glow, reflection or vignette; 22 px Pause button and 22 px ‹ › arrows on either side.

How it works

You write a plain <ul class="cmq-items"> list (at least three <li>). The script turns it into as many .cmq-face faces, set in a ring with rotateY(i × 360°/N) translateZ(r); the radius r = (width/2) / tan(π/N) is computed so the faces touch, whatever their number. The .cmq-drum parent keeps the 3D (transform-style: preserve-3d) and a 700 px perspective gives depth.

On every frame the drum turns at 54°/s (speed based on elapsed time) and each face gets an opacity and brightness that follow the cosine of its angle: the front face is crisp, back faces dim without disappearing. Dragging anywhere in the scene, with a mouse or a finger, spins the drum (0.35° per pixel); on release it keeps the gesture's momentum, then returns to cruising speed.

Hovering the scene or moving keyboard focus into it slows the drum to a stop. The Pause button freezes it; while paused, the ‹ › arrows appear and bring the next or previous face to the front, announced in an aria-live area. Each .fx-0832-root has its own drum, and the loop stops when the scene leaves the screen.

Accessibility

  • Readable content: the .cmq-items list stays in the page, visually hidden but read by screen readers; the 3D faces, which copy it, carry aria-hidden="true". The scene is a region with aria-roledescription="carousel" named by aria-label.
  • Stopping: hover and keyboard focus stop the rotation; the Pause button (aria-pressed) freezes it until the next click (WCAG 2.2.2).
  • Reduced motion: under prefers-reduced-motion: reduce, the drum never spins on its own; the ‹ › arrows are shown, each click turns one face (0.38 s transition) and announces its text. Dragging still works, without momentum: on release the drum settles on the nearest face.
  • Touch: touch-action: pan-y lets the page scroll when the finger moves vertically; only a horizontal drag spins the drum.

Browser compatibility

CSS 3D transforms (preserve-3d, perspective), Pointer Events with setPointerCapture, IntersectionObserver (feature-tested). Measured on 2026-11-08 in Chromium and Firefox: 54°/s rotation, stop on hover, focus and button, two independent copies, arrows under reduced motion, finger drag at 390 px. Playwright's WebKit flattens 3D: rotation and controls work there, but the 3D rendering could not be checked for Safari.

Chrome 55+✓ Full (measured)
Firefox 59+✓ Full (measured)
Safari 13+✓ Not verified (3D flattened in the test WebKit)
Edge 79+✓ Full (same engine as Chrome)
Safari iOS 13+✓ Not verified
Chrome Android 55+✓ Finger drag measured in emulation

Without transform-style: preserve-3d, an @supports rule lays the faces flat side by side. Without JavaScript, only the scene background shows and the list stays readable by screen readers.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0832-root" data-cmq-speed="54" data-cmq-face-w="120" data-cmq-face-h="88">
  <div class="cmq-scene" role="region" aria-roledescription="carousel" aria-label="…">
    <ul class="cmq-items">
      <li>…</li>
    </ul>
    <div class="cmq-perspective" aria-hidden="true"><div class="cmq-drum"></div></div>
    <div class="cmq-reflection" aria-hidden="true"></div>
    <div class="cmq-glow" aria-hidden="true"></div>
    <div class="cmq-vignette" aria-hidden="true"></div>
    <button class="cmq-pause-btn" type="button" aria-label="…" aria-pressed="false">⏸</button>
    <div class="cmq-nav">
      <button class="cmq-nav-btn cmq-prev" type="button" aria-label="…">‹</button>
      <button class="cmq-nav-btn cmq-next" type="button" aria-label="…">›</button>
    </div>
    <div class="cmq-live" aria-live="polite" aria-atomic="true"></div>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Faces (HTML — <li> of .cmq-items) 8 demo words At least three items. Text and inline elements (<b>, a small <img>) are copied onto the faces; links are not clickable there (faces are hidden and out of the keyboard order).
Speed (HTML — data-cmq-speed on .fx-0832-root) 54 (degrees per second) From 0 to 360. At 0, the drum only turns when dragged.
Face size (HTML — data-cmq-face-w, data-cmq-face-h) 120 × 88 px The drum radius follows: more or wider faces make a bigger drum.
Scene height (CSS — .cmq-scene and .cmq-perspective) 300 px and 260 px Override both heights for a low strip or a small pill.
Depth (CSS — perspective of .cmq-perspective) 700px Lower = stronger 3D, higher = flatter drum.
Colors (CSS — .cmq-face-inner, .cmq-label, .cmq-glow) Violet #a78bfa, background #0a0814 Border, background and text glow of the faces, central glow; .cmq-reflection and .cmq-vignette can be hidden.

FAQ

The code sets no limit, but past a dozen the radius grows fast (12 faces of 120 px give a 224 px radius): lower data-cmq-face-w or enlarge the scene so the drum is not clipped.
Anything that flattens the 3D context breaks it: overflow: hidden or filter set on .cmq-drum itself, or a parent with transform-style: flat between the perspective and the drum. Keep .cmq-perspective as the direct parent of .cmq-drum.
Pause the drum: the ‹ › arrows move one face at a time from the current position, and the .cmq-live area announces its text. Under reduced motion, they are always shown.