Prism Rotation
Six translucent 100 × 120 px panels, lettered A to F, turn around a vertical axis, one turn every 6 s. Their 50 px radius is shorter than a closed hexagon's: the faces cross each other and blend their colors like a paper lantern. Pure CSS, zero JavaScript, held at a fixed view when the user reduces motion.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. 3D has 50 effects, including 8 free. Explore the category →
3 usage examples



How it works
The .fx-0016-root root (#0a0a0f background, at least 300 px tall) centers .prism-container, which carries the camera (perspective: 800px). Inside it, .prism measures 100 × 120 px, keeps its children in 3D (transform-style: preserve-3d) and turns: rotatePrism, from rotateY(0) to rotateY(360deg) in 6 s, linear, forever. The six 100 × 120 px .prism-face elements are stacked, then placed with :nth-child: rotateY(0deg), 60deg, 120deg… up to 300deg, each followed by translateZ(50px). Each panel has its own color at 70% opacity (indigo, violet, magenta, pink, cyan, green), a 1 px border and its 12 px letter.
It all comes down to the radius. For six 100 px panels to close a hexagon, they would have to be pushed 100 × √3 / 2 ≈ 86.6 px from the center. At 50 px, each panel overhangs its slot on both sides and cuts through its two neighbors: you do not see a closed prism but a lantern of crossing planes, where the translucent colors overlap and blend. The back sides stay visible (no backface-visibility: hidden): letters on the far panels show mirrored through the near ones. For a true closed prism, raise translateZ to 87px in all six rules, as in the postcard stand and the poster column above. Measured: about 10% of the block's pixels change every 0.8 s in Chromium 151 and Firefox 153.
Crossing planes force the browser to split them so it can draw them in depth order, and each engine does it its own way. On the same still frame (reduced motion), 4.9% of pixels differ between Chromium and Firefox: the lantern is the same, but the stacking of colors where panels cross is not identical. With the 87 px radius, the panels no longer cross and that gap disappears. As with any CSS 3D, nothing may flatten .prism (overflow, filter, opacity below 1 on the element itself). 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-0016-rootstops the rotation and turns the lantern by-20deg: a fixed view where the crossing faces stay readable. Measured in Chromium and Firefox: 0 running animations, 0% of pixels changing over 0.8 s (10.4% without the preference). - Continuous motion: the rotation lasts longer than 5 s; next to text people need to read, provide a way to stop it (WCAG 2.2.2), for instance
animation-play-state: pausedon hover or on a button. Reduced motion already stops it. - Readability: at a 50 px radius, a panel is never fully visible on its own; do not put information there that must be read (a letter, a pattern is fine). For posters or cards meant to be read, close the prism (87 px radius) and repeat the essentials outside it.
- Decorative content: in a banner, put
aria-hidden="true"on the root, as in the examples; the list of contents (the six evenings, the six cards) is better offered as text too.
Browser compatibility
CSS (3D transform, preserve-3d, perspective, @keyframes), zero JavaScript, no dependencies. Measured in Chromium 151 and Firefox 153: same rotation, two independent copies, stops under reduced motion, 0 errors; where panels cross, the color stacking order differs (4.9% of pixels on the still frame). Playwright's WebKit 26.5 flattens all CSS 3D: the real Safari was not verified.
Without preserve-3d (or if .prism is flattened by overflow, filter or opacity < 1), the six panels collapse into one plane and the rotation only shows a rectangle that widens and narrows. The letters stay visible.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0016-root">
<div class="demo-3d">
<div class="prism-container">
<div class="prism">
<div class="prism-face">A</div> … (6 panels, A to F)
</div>
</div>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Radius (CSS — translateZ(50px) in the six .prism-face:nth-child(n) rules) |
50 px (crossing panels) | 87px closes the hexagon: a clean prism, each face readable in turn. Between 60 and 80 px, the panels barely touch at the edges. Beyond 87 px, they move apart and leave gaps. |
Speed (CSS — 6s in the animation of .prism) |
6 s | 10 to 12 s for panels meant to be read (posters, cards); 4 s for an ornament. Keep linear for a column that turns without a jolt. |
Panel size (CSS — width / height of .prism, .prism-face) |
100 × 120 px | The radius that closes the prism is width × 0.866: 120 px wide panels need translateZ(104px). Height is free. |
Colors (CSS — background of each :nth-child) |
indigo, violet, magenta, pink, cyan, green at 70% | Neighboring hues at 50-60% make a warm lantern; opaque backgrounds (90% and up) suit the closed prism, where you want to read one panel at a time. |
Perspective (CSS — perspective on .prism-container) |
800px | 500 px strengthens the depth (the front panel looks clearly larger); 1,400 px gives an almost flat column. |
| Number of panels (HTML and CSS) | 6, every 60° | For 8 panels: one angle every 45° and, to close it, a radius of width / (2 × tan 22.5°) ≈ 1.21 × width. Add one :nth-child rule per panel. |
FAQ
translateZ(50px) with translateZ(87px) in all six rules.