JavaScript + CSS 3D Wheel of Fortune
Talking about Wheel of Fortune game, Css, Html and Javascript.
I already published ways to display a wheel of fortune using Phaser, but this time I am going to build a 3D wheel of fortune using only JavaScript and CSS.
You can not only see it in action here, but also watch a full video where I build it from scratch, step by step. Please subscribe to my brand new YouTube channel, where I am going to post a lot of tutorials and experiments.
Back to the wheel of fortune, instead of drawing a flat circle and rotating it, I decided to create a cylinder made of flat divs placed around a central axis, a bit like a carousel or a 3D slot machine.
Each tile represents one sector of the wheel. When you spin it, the entire wheel rotates around the Y axis, revealing each tile one after another.
Let’s see it in action:
Press “Spin” button and see what happens.
Tiles are a series of div with a bit of trigonometry.
By giving the container a perspective and telling the browser to preserve the 3D context, the result looks like a real rotating object.
When the wheel spins, you can actually see the tiles move in depth, disappear on one side, and reappear on the other, just like a real-life drum or carousel.
The wheel doesn’t physically “spin” each tile individually: it’s the whole cylinder that rotates around its central Y axis.
When the “SPIN” button is pressed, a few things happen behind the scenes:
– A random result is chosen, so the wheel knows where it should stop.
– A random number of full rotations is added, so it won’t stop too soon and will look naturally unpredictable.
– A CSS transition is applied to the entire wheel, changing its rotation angle smoothly over a few seconds.
While the transition runs, the wheel continuously updates its visual rotation thanks to the browser’s 3D rendering engine.
When the animation ends, a simple trick resets the wheel’s rotation to the final resting position, making sure the next spin starts from there without accumulating extra rotations.
Look at the source code, in a single HTML file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wheel of fortune in JavaScript + CSS</title>
<style>
body {
display: grid;
place-items: center;
height: 100vh;
margin: 0;
background-color: #1f1f1f;
}
#wheelcontainer {
position: relative;
perspective: 1000px;
}
#wheel {
width: 100%;
height: 100%;
position: absolute;
transform-style: preserve-3d;
}
.wheeltile {
position: absolute;
border: 1px solid black;
font-size: 80px;
font-family: sans-serif;
font-weight: bold;
display: grid;
place-items: center;
opacity: 0.8;
}
#spin {
font-size: 24px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="wheelcontainer">
<div id="wheel"></div>
</div>
<button id="spin">SPIN</button>
<script>
// wheel elements
const wheelElement = document.getElementById('wheel');
const containerElement = document.getElementById('wheelcontainer');
const spinButton = document.getElementById('spin');
// amount of tiles
const tiles = 10;
// tile size
const tileWidth = 200;
const tileHeight = 150;
// circle geometry and trigonometry
const deltaAngle = 360 / tiles;
const z = Math.round((tileWidth / 2) / Math.tan(Math.PI / tiles));
// is the wheel spinning?
let spinning = false;
// final wheel angle;
let finalAngle;
// set element styles dynamically
containerElement.style.width = tileWidth + 'px';
containerElement.style.height = tileHeight + 'px';
wheelElement.style.transform = 'translateZ(' + (z * -1) + 'px)';
// build all tiles
for (let i = 0; i < tiles; i ++) {
// each tile is a div
const newTile = document.createElement('div');
// add wheeltile class to new tile
newTile.classList.add('wheeltile');
// set tile width and height
newTile.style.width = tileWidth + 'px';
newTile.style.height = tileHeight + 'px';
// assign the tile a random color
newTile.style.backgroundColor = randomColor();
// transform the tile
newTile.style.transform = 'rotateY(' + deltaAngle * i + 'deg) translateZ(' + z + 'px)';
// put some content in the tile
newTile.innerText = i;
// append tile to wheel
wheelElement.appendChild(newTile);
}
// function to randomly generate a random light color
function randomColor() {
const r = Math.floor(Math.random() * 155) + 100;
const g = Math.floor(Math.random() * 155) + 100;
const b = Math.floor(Math.random() * 155) + 100;
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
// when the button is clicked...
spinButton.addEventListener('click', () => {
// if the wheel is spinning, do nothing
if (spinning) {
return;
}
spinning = true;
// toss a random result
const randomResult = Math.floor(Math.random() * tiles);
const randomRounds = 5 + Math.floor(Math.random() * 15);
const randomSeconds = 5 + Math.floor(Math.random() * 5);
// just to show you I am right :)
spinButton.innerText = 'Will end on tile ' + randomResult;
// compute the final angle
finalAngle = randomResult * deltaAngle + 360 * randomRounds;
// apply the proper style to wheel element
wheelElement.style.transition = 'transform ' + randomSeconds + 's';
wheelElement.style.transform = 'translateZ(-' + z + 'px) rotateY(-' + finalAngle + 'deg)';
});
// function to be executed when the rotation is over
wheelElement.addEventListener('transitionend', () => {
spinning = false;
// reset button and wheel transform
spinButton.innerText = 'SPIN';
finalAngle = finalAngle % 360;
wheelElement.style.transition = 'none';
wheelElement.style.transform = 'translateZ(-' + z + 'px) rotateY(-' + finalAngle + 'deg)';
});
</script>
</body>
</html>Now you can copy/paste into your projects and have your spinning 3D wheel of fortune.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.