3DFree

Rotating Cube

A 100 px CSS cube with six translucent, colored, labeled faces, set at a three-quarter angle, turns toward the pointer as soon as it enters its area, then goes back to its pose when it leaves. It can also be turned with a finger and with the keyboard's arrow keys; hidden faces show through the others. 1.8 KB of JavaScript, one cube per copy, and no tilt following the pointer when the user reduces motion.

CSS3DInteractive

Updated

Front
Back
Right
Left
Top
Bottom
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

Conjugation · 3rd grade

The pronoun die

Conjugate finir in the present tense with the pronoun on the front face.

Your answer:

Turn the die with the mouse, a finger or the arrow keys.

je
tu
il / elle
nous
vous
ils / elles

① Pronoun die — conjugation exercise on a 3rd-grade tutoring website

WhenA giant die to the right of the instruction "Conjugate finir in the present tense with the pronoun on the front face": je, tu, il / elle, nous, vous, ils / elles. The child turns the die with the mouse, a finger or the arrow keys, then writes the answer.
WhyThe child orients the die and sees the pronouns of the opposite faces through it: it is an object you handle, not an animation that scrolls by. The self-turning cube (Cube Auto-Rotation) would take the gesture away.
SettingsListening area (.cube-container) enlarged to a square the full height of the scene, with the cube centered in it: the tilt reaches 30-40°; 120 px cube (faces and translateZ(60px)), 100 px on phones; original colors, pronouns in 20 px bold; touch-action: pan-y on the large area.

Move #2027-118 · truck 2

Box no. 14: check the markings

  • No. 11 · Booksloaded ✓
  • No. 12 · Dishesloaded ✓
  • No. 13 · Linenloaded ✓
  • No. 14 · Kitchento check
Kitchen
FRAGILE
This side up ↑
Glasses
12 kg
Room: living room

② Moving box labeled on all six faces — inventory app of a moving company

WhenBox no. 14 card: faces "Kitchen", "FRAGILE", "This side up ↑", "Glasses", "12 kg", "Room: living room"; on the left, the list of boxes already loaded. You turn the box to check each marking.
WhyA box is read on all its faces: turning it with the mouse shows that "This side up" and "FRAGILE" really are on two different faces, which a flat card cannot show.
SettingsSame enlarged area and 120 px cube; cardboard-colored faces at 86% (sides one shade darker), brown border, dark brown 15 px bold text; "FRAGILE" in red.

Stonemasonry workshop · glossary

Each face has a name and a job when laid

Bed: the face set on the mortar.Upper bed: takes the stone above.Face: the one you see on the wall.

Face
Tail
Right joint
Left joint
Upper bed
Bed

③ A dressed stone and the names of its six faces — website of a stone-cutting workshop

WhenA beige limestone block: face at the front, upper bed on top, bed underneath, joints on the left and right, tail at the back; on the left, the glossary "Each face has a name and a job when laid".
WhyThe stonemason's vocabulary names opposite faces (bed and upper bed): the translucency lets you read the bottom face while looking at the top one.
SettingsSame enlarged area and 120 px cube; limestone rgba(224, 208, 174, .8) faces, gray-brown border, names in 13 px Georgia; dark gray workshop background.

How it works

The .fx-0001-root root is 250 px tall, rgb(26, 26, 36) background. It centers .cube-container, a 100 px square that is both the camera (perspective: 600px) and the area that listens to the pointer. Inside it, .cube keeps its six faces in 3D (transform-style: preserve-3d) and rests at rotateX(-20deg) rotateY(-30deg): at three-quarters, you see the front, the right side and the top. Each 100 px .cube-face is pushed 50 px in its direction (translateZ(50px) after a 90 or 180° rotation) and has its own color at 80%: indigo front, violet back, magenta right, cyan left, green top, pink bottom. Back sides stay visible: through the front faces, you read the far ones mirrored.

The script finds every .fx-0001-root on the page and gives each its own cube. On pointermove and pointerdown (mouse, finger or pen), it measures the pointer's distance from the area's center and turns it into two angles: rotateX(-y / 5) and rotateY(x / 5). The cube therefore turns toward the pointer, one degree every 5 px: ±10° at most in the 100 px area, ±36° if you enlarge the area to 360 px as in the examples. The 0.1 s transition (ease-out) smooths the motion, and the jump on entry: when the pointer arrives in a small area, the cube goes from its three-quarter pose to an angle close to face-on. On pointerleave, it goes back to three-quarters. The area has touch-action: none: on a touch screen, sliding a finger over it turns the cube instead of scrolling the page, and it settles back when the finger lifts.

With the keyboard, Tab reaches the area (tabindex="0", 2 px #a5b4fc outline and brighter edges on focus): the arrow keys turn the cube in 15° steps, with no limit, so every face can be reached; Escape or losing focus puts it back at three-quarters. Measured in Chromium 151 and Firefox 153: pointer 40 px right of and 30 px above the center → rotateX(6deg) rotateY(8deg); right, right, up → rotateX(-5deg) rotateY(0deg); finger slide (Chromium, touch events) → the cube follows, the page stays put; two copies on one page → only the hovered one moves; 0 errors. Playwright's WebKit flattens all CSS 3D: rendering on a real Safari was not verified.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, the script stops following the pointer and the CSS removes the transition (a block scoped to .fx-0001-root): the cube stays at three-quarters when hovered. The arrow keys still turn it, on request and in steps, with no animation. Measured in Chromium and Firefox: the cube keeps its pose when the pointer passes over it, and 0% of pixels change over 0.8 s.
  • Keyboard: Tab reaches the cube, each arrow press turns it 15°, Escape puts it back; the focus outline only shows with the keyboard. The label (aria-label) mentions the arrow keys: rewrite it in your page's language, as in the examples.
  • Touch: touch-action: none on a 100 px area does not stop the page from scrolling elsewhere. If you make the area much larger, switch it to touch-action: pan-y (as in the examples): vertical scrolling stays possible and a horizontal slide turns the cube.
  • Face content: the labels are real text, read by screen readers in HTML order (front, back, right, left, top, bottom). A turned face shows mirrored through the others: write any essential information next to the cube.

Browser compatibility

CSS (3D transform, preserve-3d, perspective, touch-action) + 1.8 KB of JavaScript (pointer events, keydown, matchMedia), no dependencies. Measured in Chromium 151 and Firefox 153: same cube (0.57% difference on the still frame), orientation with the mouse and keyboard, two independent copies, 0 errors; finger slide measured in Chromium. Playwright's WebKit 26.5 flattens all CSS 3D: the real Safari was not verified.

Chrome 55+✓ Full (measured)
Firefox 59+✓ Full (measured)
Safari✓ Not verified on a real Safari
Edge 79+✓ Full (Chromium engine)
Mobile iOS✓ Not verified on a real Safari
Android Chrome 55+✓ Full (Chromium engine, finger slide measured)

Without JavaScript, the cube stays still at its three-quarter pose, three faces visible. Without preserve-3d, the six faces stack flat and only the last one (the pink bottom) stays readable.

The code

Copy the three blocks into your page. No dependencies.

HTML
<div class="fx-0001-root">
  <div class="demo-3d">
    <div class="cube-container" tabindex="0" role="group" aria-label="3D cube: point at it, or use the arrow keys">
      <div class="cube">
        <div class="cube-face front">Front</div>
        <div class="cube-face back">Back</div>
        <div class="cube-face right">Right</div>
        <div class="cube-face left">Left</div>
        <div class="cube-face top">Top</div>
        <div class="cube-face bottom">Bottom</div>
      </div>
    </div>
  </div>
</div>
CSS
.fx-0001-root,
.fx-0001-root *,
.fx-0001-root *::before,
.fx-0001-root *::after {
  box-sizing: border-box
}

.fx-0001-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;
}

.demo-3d {
  width: 280px;
  height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.cube-container {
  width: 100px;
  height: 100px;
  perspective: 600px;
  /* A finger turns the cube instead of scrolling the page (100 px area). */
  touch-action: none;
  outline: none;
}

.cube-container:focus-visible {
  outline: 2px solid #a5b4fc;
  outline-offset: 22px;
  border-radius: 6px;
}

.cube-container:focus-visible .cube-face {
  border-color: rgba(255, 255, 255, 0.9);
}

.cube {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
  transform: rotateX(-20deg) rotateY(-30deg);
  transition: transform 0.1s ease-out;
}

.cube-face {
  position: absolute;
  width: 100px;
  height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 14px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  backface-visibility: visible;
}

.cube-face.front {
  background: rgba(99, 102, 241, 0.8);
  transform: translateZ(50px);
}

.cube-face.back {
  background: rgba(139, 92, 246, 0.8);
  transform: rotateY(180deg) translateZ(50px);
}

.cube-face.right {
  background: rgba(217, 70, 239, 0.8);
  transform: rotateY(90deg) translateZ(50px);
}

.cube-face.left {
  background: rgba(6, 182, 212, 0.8);
  transform: rotateY(-90deg) translateZ(50px);
}

.cube-face.top {
  background: rgba(16, 185, 129, 0.8);
  transform: rotateX(90deg) translateZ(50px);
}

.cube-face.bottom {
  background: rgba(244, 63, 94, 0.8);
  transform: rotateX(-90deg) translateZ(50px);
}

/* Reduced motion: the cube no longer follows the pointer and stays at its three-quarter pose;
   the arrow keys still turn it, in 15° steps, with no transition. */
@media (prefers-reduced-motion: reduce) {
  .fx-0001-root .cube { transition: none }
}
JavaScript (fx-0001)
(function() {
  // Rotating Cube: the cube turns toward the pointer (mouse or finger) and goes back to its three-quarter pose when it leaves.
  // With the keyboard (Tab), the arrow keys turn it in 15° steps; Escape puts it back at three-quarters.
  // One cube per .fx-0001-root: several copies can live on the same page.
  var reduce = window.matchMedia('(prefers-reduced-motion: reduce)');
  var REST_X = -20, REST_Y = -30;

  document.querySelectorAll('.fx-0001-root').forEach(function(root) {
    var cube = root.querySelector('.cube');
    var zone = root.querySelector('.cube-container');
    if (!cube || !zone) return;
    var keyX = REST_X, keyY = REST_Y;

    function turn(x, y) {
      cube.style.transform = 'rotateX(' + x + 'deg) rotateY(' + y + 'deg)';
    }

    function rest() {
      keyX = REST_X;
      keyY = REST_Y;
      turn(REST_X, REST_Y);
    }

    function follow(e) {
      if (reduce.matches) return; // reduced motion: no tilt following the pointer
      var rect = zone.getBoundingClientRect();
      var x = e.clientX - rect.left - rect.width / 2;
      var y = e.clientY - rect.top - rect.height / 2;
      turn(-y / 5, x / 5);
    }

    zone.addEventListener('pointermove', follow);
    zone.addEventListener('pointerdown', follow);
    zone.addEventListener('pointerleave', rest);

    zone.addEventListener('keydown', function(e) {
      if (e.key === 'Escape') { rest(); return; }
      var step = { ArrowUp: [15, 0], ArrowDown: [-15, 0], ArrowLeft: [0, -15], ArrowRight: [0, 15] }[e.key];
      if (!step) return;
      e.preventDefault(); // the arrow keys turn the cube, they do not scroll the page
      keyX += step[0];
      keyY += step[1];
      turn(keyX, keyY);
    });
    zone.addEventListener('blur', rest);
  });
})();

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Listening area size (CSS — width / height of .cube-container) 100 px, the cube's size The tilt equals the distance from the center divided by 5: a 100 px area gives ±10°, a 300 px area ±30°. To enlarge the area without enlarging the cube, center .cube in it (display: flex, and a fixed size for .cube), as in the examples.
Sensitivity (JS — -y / 5 and x / 5 in follow) 1° every 5 px / 3 makes the cube livelier (±17° in 100 px); / 8 calmer. To cap the angle, wrap it in Math.max(-30, Math.min(30, …)).
Rest pose (JS — REST_X, REST_Y; CSS — transform of .cube) -20°, -30° Change both together: the CSS sets the pose before the script runs, the script sets the pose it returns to. -35 / 45 shows more of the top; 0 / 0 presents the front face flat.
Keyboard step (JS — the 15 values in the step table) 15° per press 90 moves from one face to the next on each press, handy for a die meant to be read face by face.
Cube size (CSS — 100px on the faces, translateZ(50px)) 100 px The push is half the side: 120 px side, translateZ(60px) in all six face rules.
Faces (HTML and CSS — text and background of each .cube-face) six colors at 80%, labels Front to Bottom Below 0.6 opacity, the opposite face reads clearly through; at 1, the cube turns solid and the hidden faces disappear.

FAQ

Why does the cube barely move in the demo?

Because the area that listens to the pointer is only 100 px, the size of the cube: at 1° every 5 px, the tilt never exceeds 10°. Enlarge .cube-container (and center the cube in it) for a wider motion, as in the three examples, or divide by a smaller number in the script.

Can I put several cubes on the same page?

Yes. The script goes through every .fx-0001-root on the page and gives each its own area, cube and keyboard state; it relies on no id. Measured with two copies: only the hovered one moves.

How is it different from Cube Auto-Rotation (fx-0011)?

Cube Auto-Rotation turns on its own, endlessly, with six faces of the same color: an object in midair. Rotating Cube only moves when you orient it, with faces of different colors and names: an object you handle to read each side.