webdevpuneet.com

Dice Roller — CSS 3D Dice Roll Animation (JS)

Dice Roller · Games · Plain HTML, CSS & JS · Live preview

CategoryGames

For supporters

Source Code

Want the source code?

Play the game for free. Become a WebDevPuneet supporter to unlock the complete HTML, CSS and JavaScript source code, the exports (HTML, React, Vue, Angular, Tailwind) and Fork.

Play freeSource code: Patreon

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Real CSS 3D cube
Six faces in a preserve-3d scene with perspective — a genuine 3D object, not a flat sprite.
Grid-area pip layouts
Each face's dots use named CSS grid areas for clean, declarative die faces.
Transform-based roll
Rolling is one CSS transition to a known orientation plus extra full turns to tumble.
Lands on the right face
The final rotation is the chosen face's base plus whole 360° turns, so it settles exactly.
Continuous forward spin
An accumulating spin count means the cube never snaps backwards between rolls.
Honest randomness
Math.random() picks the value before the animation, so the visual always matches the result.
Roll guard
The button disables during the tumble and re-enables with the result.
No images, no library
Pure HTML/CSS/JS — themeable and dependency-free.

About this UI Snippet

Dice Roller — A Real 3D CSS Cube That Tumbles to a Random Face

Screenshot of the Dice Roller snippet rendered live

A rolling 3D die is a small piece of delight that's also a perfect lesson in CSS 3D transforms. This snippet builds a real cube — six faces with correct pip layouts — that tumbles and lands showing a random face, in pure HTML, CSS, and a few lines of vanilla JavaScript, with no library and no images.

A genuine 3D cube, not a flat sprite

The cube is six div faces positioned in 3D space. The container has transform-style: preserve-3d so its children render in a shared 3D scene, and each face is pushed out from the centre with translateZ(48px) after being rotated to point in its direction — front, back, left, right, top, bottom. The scene has perspective so the cube looks dimensional rather than isometric. This is the canonical way to build a CSS cube, and it means the die is a real object you can rotate to any orientation.

Pips placed with CSS grid areas

Each face's dots are laid out with CSS grid template areas — a 3×3 grid where named cells hold the pips for that number. One pip sits in the centre cell; two sit in opposite corners; five fills the corners plus centre; and so on. Using named grid areas (rather than absolute positioning) makes each face's layout declarative and easy to read, and it's the cleanest way to get the classic die-face arrangements without hand-tuning pixel positions.

Rolling by rotating to a known orientation

The roll is one CSS transition. For each face there's a base rotateX/rotateY that brings it to the front, matching the face transforms. Rolling picks a random 1–6, looks up that base rotation, and adds several full 360° turns so the cube visibly tumbles before settling — landing exactly on the chosen face because the final orientation is a known value plus whole turns. Accumulating the spin count across rolls means the cube always rotates forward, never snapping backwards, so consecutive rolls look continuous.

Honest randomness and a settled result

The result is chosen with Math.random() *before* the animation, so the visual outcome always matches the reported number — the die isn't faked after the fact. The button disables during the ~1s tumble and re-enables with the result text when it lands, so users can't spam mid-roll and the announced value always corresponds to the resting face.

Drop-in and adaptable

The whole thing is self-contained and themeable with CSS. Use it for a board-game UI, a randomiser, a "pick for me" control, or just a tactile bit of fun. It's also a clear, complete reference for CSS 3D cubes, preserve-3d, and transform-based animation that you can adapt into any 3D interaction.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA 3D die renders on a dark stage with a Roll button.
  2. 2
    Roll itClick Roll (or the die) and the cube tumbles for about a second, landing on a random face.
  3. 3
    Read the resultThe text below shows the rolled value, which always matches the resting face.
  4. 4
    Roll againEach roll adds more turns so the cube keeps tumbling forward continuously.
  5. 5
    Restyle itChange the face gradient, pip colour, size, or perspective to fit your design.
  6. 6
    Hook into your gameRead the chosen number in roll() to drive a board game, randomiser, or picker.

Real-world uses

Common Use Cases

Board and tabletop game interfaces
Drive turns with a tactile roll, using a real six-faced cube in a preserve-3d scene with correct pip layouts.
Random pick tools
Use the result to choose an option, pairing with a spin wheel for lists with more than six choices.
Quizzes and classrooms
Pick a random student or question, rolling with one CSS transition to a known orientation plus extra full turns.
Gamified onboarding
Add playful chance to a flow, with a scratch card reveal or confetti button rewarding the outcome.
Party apps and 3D learning
Leave a choice to fate, and study named grid-template-areas for pip layouts alongside a 3D card tilt for a different kind of depth.
Related: GSAP Draggable Inertia
See the GSAP Draggable Inertia for a related animations pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Each face has a base rotateX/rotateY that brings it to the front, matching the CSS transforms that position the faces. Rolling picks a random 1–6, looks up that base rotation, and adds several full 360° turns. Because a full turn returns to the same orientation, the cube tumbles and then settles exactly on the chosen face — the final transform is "known landing orientation + whole turns."

Each face is a 3×3 CSS grid with named grid-template-areas, and the pips are placed into named cells (centre for 1, opposite corners for 2, all corners plus centre for 5, and so on). Declarative grid areas are cleaner and more readable than hand-tuned absolute coordinates, and they reproduce the classic die-face layouts exactly.

It's genuinely random and decided first. Math.random() picks the value before the animation starts, the cube is rotated to land on that face, and the same number is reported when it settles. The visual outcome and the announced result always match — nothing is changed after the tumble.

The roll is a ~1-second CSS transition. Disabling the button (and ignoring clicks on the cube) during that window prevents a new roll from interrupting the current animation, which would make the cube jump and could desync the displayed result from the resting face. It re-enables with the result text once the die settles.

In React, hold the current rotation/result in state and set the cube's transform style in the click handler, using a timeout to re-enable; in Vue, bind :style with a ref; in Angular, bind [style.transform] with a component property. The FACE_ROT lookup and rotation math are framework-agnostic — only the state and the transform binding move into the framework.