3DFree

VR Card Gallery

Translucent 70 × 90 px glass cards, each with an emoji at its center, placed in a circle by JavaScript (the angle is computed, so add as many cards as you like) and turning endlessly around the vertical axis, one turn every 12 s. Their back sides stay visible through the glass: a light gallery rather than a carousel of opaque tiles. A CSS 3D simulation on a flat screen, with no headset and no WebXR. 0.6 KB of JavaScript, still when the user reduces motion.

CSS 3DCarouselGlass

Updated

Hover or click the scene to interact

This effect is free — the 3D category contains 50 effects total, including 8 free. Effect.Labs has 840 vanilla effects. Explore the category →

Usage examples

Les Tilleuls youth centerBack to school 2027

Workshop sign-ups: Saturday 9 am – 12 pm

Ages 8 to 17 · first session free

① The youth center's five workshops turning — back-to-school page of a youth center

WhenOn a midnight-blue background, five glass cards turn: visual arts, photo, video, music, jewelry making; below, "Workshop sign-ups: Saturday 9 am – 12 pm".
WhyThe translucent glass shows the front workshop and the ones behind at the same time: the whole program stays present, unlike the opaque tiles of 3D Carousel.
SettingsWorkshop label under the emoji (9.5 px bold); gallery enlarged ×1.45 with transform: scale() on .xr-vr-gallery (×1.15 on phones); midnight-blue background with indigo, pink and cyan patches so the glass shows.
La Ressourcerie du CanalReuse and creative recycling

Charity shop open Wednesday and Saturday, 10 am – 6 pm

Drop-offs on Tuesday · 14 quai du Canal

② The five departments of a creative reuse shop — home page of a reuse charity

WhenFive cards: art supplies, frames, DVDs, vinyl records, costume jewelry; under the ring, the charity shop's opening hours and drop-off day.
WhyThe number of cards is changed in the HTML without touching the angles, which the JavaScript computes: adding or removing a department is all it takes to update the page.
SettingsCards recolored green and ochre (rgba(21, 128, 61, .42) → rgba(161, 98, 7, .3)) on a cream background mottled with yellow, green and red; labels under the emoji; same enlargement.
Flower parade 2027Saint-Amand carnival

Sunday 3 pm: 5 floats, 3 brass bands

Around the Grand-Place, then along the river

③ The flower parade floats going around — a small town's carnival website

WhenFive cards with the floats' themes (sunflowers, the peacock, the circus, the dragon, tulips) turn under "Flower parade 2027"; subtitle "Sunday 3 pm: 5 floats, 3 brass bands".
WhyThe continuous round in perspective suggests the procession going around the square; the glass cards stay light against a festive background.
SettingsPink and gold cards (rgba(244, 114, 182, .38) → rgba(250, 204, 21, .22)), golden border; plum background with confetti and colored halos; same enlargement.

How it works

The .fx-0043-root root is 250 px tall, rgb(26, 26, 36) background, overflow: hidden. .xr-vr-gallery fills it and carries the camera (perspective: 600px). At the center, .xr-vr-gallery-inner (200 × 140 px) keeps its children in 3D (transform-style: preserve-3d) and turns from rotateY(0) to rotateY(360deg) in 12 s, linear, forever. The .xr-vr-gcard elements are all centered in the same spot (left: 50%, top: 50% and negative margins): 70 × 90 px, 8 px corners, an indigo → violet gradient at 30 and 20% opacity, a 30% indigo border, a 4 px backdrop blur, and a 1.5 rem emoji.

The script builds the circle. For each .fx-0043-root on the page, it counts the cards and gives card number i rotateY(i / count × 360deg) translateZ(80px): with five cards, one angle every 72°, 80 px from the center. Adding or removing a card in the HTML is enough, the angles follow. The radius, though, stays fixed: up to six 70 px cards, they do not touch; from seven on, they overlap (the distance between neighbors drops below 70 px). Since the back sides are not hidden, the far cards stay visible through the glass, mirrored: the whole gallery is on screen at once. Measured in Chromium 151 and Firefox 153: about 11.7% of the block's pixels change over 0.8 s, two galleries on one page each get their five cards placed, 0 errors.

The name comes from the catalog: there is no virtual reality in this code. No WebXR, no headset, no head tracking; it is a CSS 3D gallery seen on an ordinary screen, which reacts to neither mouse nor keyboard. The backdrop blur only shows when there are colors behind the cards, hence the mottled backgrounds in the examples. Playwright's WebKit flattens all CSS 3D: rendering on a real Safari was not verified.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, a block scoped to .fx-0043-root stops the rotation and turns the gallery by -18deg: a fixed view where the front card and its neighbors stay readable. Measured in Chromium and Firefox: 0 running animations, 0% of pixels changing over 0.8 s (11.7% without the preference).
  • Continuous motion: the gallery turns endlessly and nothing stops it on hover. Next to text people need to read, add a pause (WCAG 2.2.2): .xr-vr-gallery:hover .xr-vr-gallery-inner { animation-play-state: paused }, or a button.
  • Emoji: a screen reader reads each emoji's name ("artist palette", "clapper board"…), not what it means on the page. Add a label inside the card, as in the examples, or put aria-hidden="true" on the root and the list as text next to it.
  • No interaction: the cards are not links. If they must lead somewhere, keep the clickable list outside the ring: a turning card is a moving target.

Browser compatibility

CSS (3D transform, preserve-3d, perspective, backdrop-filter) + 0.6 KB of JavaScript that places the cards, no dependencies. Measured in Chromium 151 and Firefox 153: same gallery (0.96% difference on the still frame), two independent galleries, 0 errors. A pitfall measured in Chromium 151: under two nested rounded containers that clip (overflow: hidden + border-radius), part of the blurred cards is no longer drawn; Firefox is not affected. Playwright's WebKit 26.5 flattens all CSS 3D: the real Safari was not verified. No WebXR: no headset mode.

Chrome 76+✓ Full (measured)
Firefox 103+✓ Full (measured)
Safari✓ Not verified on a real Safari
Edge 79+✓ Full (Chromium engine)
Mobile iOS✓ Not verified on a real Safari
Android Chrome 76+✓ Full (Chromium engine)

Without JavaScript, the cards stay stacked at the center and turn together like a single card. Without backdrop-filter (Chrome before 76, Firefox before 103), the cards keep their tinted glass, with no blur. If you place the gallery in a rounded card that itself sits in a rounded block, keep only one of the two rounded containers on overflow: hidden and clip the other with clip-path: inset(0 round 12px), like the frame of the examples on this page, or remove backdrop-filter from the cards.

The code

Copy the three blocks into your page. No dependencies.

HTML
<div class="fx-0043-root">
  <div class="xr-vr-gallery">
    <div class="xr-vr-gallery-inner">
      <div class="xr-vr-gcard">&#127912;</div>
      <div class="xr-vr-gcard">&#128444;</div>
      <div class="xr-vr-gcard">&#127916;</div>
      <div class="xr-vr-gcard">&#127925;</div>
      <div class="xr-vr-gcard">&#128142;</div>
    </div>
  </div>
</div>
CSS
.fx-0043-root,
.fx-0043-root *,
.fx-0043-root *::before,
.fx-0043-root *::after {
  box-sizing: border-box
}

.fx-0043-root {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 250px;
  overflow: hidden;
  position: relative;
  background: rgb(26, 26, 36);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.xr-vr-gallery {
  width: 100%;
  height: 100%;
  perspective: 600px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.xr-vr-gallery-inner {
  position: relative;
  width: 200px;
  height: 140px;
  transform-style: preserve-3d;
  animation: xrGalleryRotate 12s linear infinite;
}

.xr-vr-gcard {
  position: absolute;
  width: 70px;
  height: 90px;
  background: linear-gradient(135deg, rgba(99, 102, 241, 0.3), rgba(139, 92, 246, 0.2));
  border: 1px solid rgba(99, 102, 241, 0.3);
  border-radius: 8px;
  left: 50%;
  top: 50%;
  margin-left: -35px;
  margin-top: -45px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  backdrop-filter: blur(4px);
}

@keyframes xrGalleryRotate {
  from {
    transform: rotateY(0deg);
  }

  to {
    transform: rotateY(360deg);
  }
}

/* Reduced motion: the gallery stops, turned slightly so the side cards stay readable. */
@media (prefers-reduced-motion: reduce) {
  .fx-0043-root .xr-vr-gallery-inner { animation: none; transform: rotateY(-18deg) }
}
JavaScript (fx-0043)
(function() {
  // VR Card Gallery: places the cards in a circle (angle = 360° / number of cards, 80 px radius).
  // One gallery per .fx-0043-root: several galleries can live on the same page.
  document.querySelectorAll('.fx-0043-root').forEach(function(root) {
    var gallery = root.querySelector('.xr-vr-gallery-inner');
    if (!gallery) return;
    var cards = gallery.querySelectorAll('.xr-vr-gcard');
    var count = cards.length;
    cards.forEach(function(card, i) {
      var angle = (i / count) * 360;
      card.style.transform = 'rotateY(' + angle + 'deg) translateZ(80px)';
    });
  });
})();

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Number of cards (HTML — the .xr-vr-gcard elements) 5 Add or remove cards: the script recomputes the angles. Beyond six, widen the radius so they do not overlap.
Radius (JS — translateZ(80px)) 80 px For n 70 px cards with no overlap, the radius must be at least 35 / sin(180° / n): 60 px for 5, 92 px for 8, 114 px for 10. Widen .xr-vr-gallery-inner accordingly.
Speed (CSS — 12s in the animation of .xr-vr-gallery-inner) 12 s per turn 18 to 24 s for a calm page background; 8 s for a lively showcase. Keep linear.
Glass (CSS — background, border and backdrop-filter of .xr-vr-gcard) indigo → violet at 30 / 20%, 4 px blur More opacity (50%) makes the far cards less visible; an 8 px blur gives frosted glass. The blur only acts on a colored background or an image.
Perspective (CSS — perspective of .xr-vr-gallery) 600px 400 px brings the camera closer: the front card grows noticeably, the far ones shrink. 1,000 px flattens the ring.
Card size (CSS — width, height and margins of .xr-vr-gcard) 70 × 90 px, −35 / −45 px margins The negative margins are half the size: for 90 × 120 px, margin-left: -45px; margin-top: -60px, and a larger radius.

FAQ

Is this virtual reality?

No. The name comes from the catalog, but the code is a CSS 3D gallery shown on an ordinary screen: no WebXR, no headset, no head or mouse tracking. It suggests a room you look around; for a real VR scene you need a WebXR library such as A-Frame or Three.js.

Why are the far cards mirrored?

Their back sides are not hidden: through the glass of the front cards, you see the backs of the far cards, so their emoji appears reversed. That is what makes it a glass gallery. To show only the cards facing you, add backface-visibility: hidden to .xr-vr-gcard.

Can I put several galleries on one page?

Yes: the script handles each .fx-0043-root separately and relies on no id. Measured with two galleries: each gets its five cards in a circle and turns on its own.