Welcome back to TheVsHub.in! Whether you are an absolute beginner or looking to sharpen your frontend skills, building small, interactive games is one of the best ways to learn.
Today, we are going to build a Dice Duel game (inspired by the core mechanic of Ludo). Two players roll a die, the game determines the winner, handles draws, plays sound effects, and includes smooth animations to make it feel responsive and fun.
By the end of this tutorial, you will understand how to combine HTML structure, CSS styling (including Grid and animations), and JavaScript logic to create a fully functional web application.
Prerequisites
You don't need much to get started:
- A text editor (like VS Code, Sublime Text, or even Notepad).
- A web browser (Chrome, Firefox, Edge, etc.).
- Basic knowledge of HTML tags, CSS properties, and JavaScript functions.
Step 1: Setting up the Project Structure
Create a new folder on your computer named dice-duel. Inside
that folder, create a file named index.html.
We are going to put all our code (HTML, CSS, and JavaScript) into this one file to keep things simple for this tutorial. In larger projects, you would usually separate them.
Let's start with the basic HTML skeleton in index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dice Duel - TheVsHub Tutorial</title>
<!-- We will add CSS here later -->
</head>
<body>
<!-- We will add HTML Content here later -->
<!-- We will add JavaScript here later -->
</body>
</html>
Step 2: Designing the HTML Skeleton
Inside the <body> tags, let's add the elements we need:
a heading, a place to show the result, the two player areas (containing
the dice), and the "Roll" button.
Paste this inside the <body>:
<h1>Ludo Duel</h1>
<!-- This container will display "Player 1 Wins!", etc. -->
<div class="result" id="resultDisplay"></div>
<div class="players">
<!-- Player 1 Area -->
<div class="player" id="player1">
<h2>Player 1</h2>
<!-- The dice dots will be generated inside this div via JS -->
<div class="dice dice1" id="d1"></div>
</div>
<!-- Player 2 Area -->
<div class="player" id="player2">
<h2>Player 2</h2>
<div class="dice dice2" id="d2"></div>
</div>
</div>
<button id="rollDice">Roll Dice</button>
Code Explanation:
-
#resultDisplay: This empty
divis crucial. JavaScript will inject text (like "Player 1 Wins!") into this element after the dice are rolled. - .players: A container to hold both player cards side-by-side.
-
.dice: These empty divs (
#d1and#d2) represent the face of the die. We will use CSS Grid to arrange dots inside them later using JavaScript.
Step 3: Making it Beautiful with CSS
Right now, the page looks very plain. Let's add the styles inside the
<head> section, within <style> tags.
We are going to use two external resources here:
- Google Fonts: To get the cool decorative font "Monoton" for the main title.
- Font Awesome: To add icons (like trophies) to the result display.
Add this link inside your <head> tag, above where the
<style> block will go:
<!-- Font Awesome for Icons --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" />
Now, add the CSS inside <style> tags in the
<head>:
@import url('https://fonts.googleapis.com/css2?family=Monoton&display=swap');
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
gap: 1.5rem;
background-color: #f8f9fa;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
h1 {
font-family: "Monoton", cursive;
font-size: clamp(2rem, 8vw, 4rem);
color: #333;
text-shadow: 2px 2px 0px #eee;
}
.players {
display: flex;
gap: 30px;
flex-wrap: wrap;
justify-content: center;
}
.player {
background: #fff;
padding: 25px;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05);
text-align: center;
width: 180px;
border: 4px solid transparent;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
#player1 {
border-color: #ff4d4d;
}
#player2 {
border-color: #2ecc71;
}
/* Animations */
.active {
transform: scale(1.1);
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
animation: pulseGlow 1s infinite;
}
.shake {
animation: shakeDice 0.5s ease-in-out;
}
@keyframes shakeDice {
0%,
100% {
transform: rotate(0deg);
}
25% {
transform: rotate(10deg);
}
75% {
transform: rotate(-10deg);
}
}
@keyframes pulseGlow {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
.dice {
width: 90px;
height: 90px;
margin: 15px auto;
border-radius: 15px;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
padding: 12px;
gap: 5px;
}
.dice1 {
background: linear-gradient(145deg, #ff4d4d, #d43f3f);
}
.dice2 {
background: linear-gradient(145deg, #2ecc71, #27ae60);
}
.dice span {
width: 12px;
height: 12px;
background-color: white;
border-radius: 50%;
align-self: center;
justify-self: center;
opacity: 0;
box-shadow: inset 0 2px 2px rgba(0, 0, 0, 0.2);
}
button {
padding: 15px 40px;
font-size: 1.2rem;
font-weight: bold;
color: white;
background-color: #333;
border: none;
border-radius: 50px;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
button:active {
transform: scale(0.95);
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.result {
min-height: 60px;
padding: 10px 30px;
font-size: 1.5rem;
font-weight: 800;
border-radius: 50px;
background: white;
display: flex;
align-items: center;
gap: 15px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
opacity: 0;
transform: translateY(-20px);
transition: all 0.4s ease;
}
.result.show {
opacity: 1;
transform: translateY(0);
}
.fa-trophy {
color: #f1c40f;
}
CSS Key Concepts:
-
Flexbox: Used on the
bodyand.playersto easily center elements and create gaps between them. -
Transitions: Applied to
.playerand.result. This makes changes happen smoothly over time, rather than instantly. - CSS Grid for Dice: This is the most efficient way to style a die face. We create a 3x3 grid. Imagine 9 cells. For a dice roll of "1", we only illuminate the cell in the exact center.
Step 4: Implementing Logic with JavaScript
Finally, let's bring it to life. This is where the magic happens. We need to handle the button click, generate random numbers, decide the winner, play sound, and update the HTML.
Add this just before the closing </body> tag:
// 1. Select DOM Elements
const btn = document.getElementById('rollDice');
const d1Container = document.getElementById('d1');
const d2Container = document.getElementById('d2');
const resBox = document.getElementById('resultDisplay');
// 2. Setup Audio
const audio = new Audio("https://cdn.pixabay.com/audio/2023/03/14/audio_7763cd5c8a.mp3");
// 3. Define Dice Layouts
const diceLayouts = {
1: [4],
2: [0, 8],
3: [0, 4, 8],
4: [0, 2, 6, 8],
5: [0, 2, 4, 6, 8],
6: [0, 2, 3, 5, 6, 8]
};
// 4. Function to draw the dice dots
function createDice(container, value) {
container.innerHTML = '';
for (let i = 0; i < 9; i++) {
const dot = document.createElement('span');
if (diceLayouts[value].includes(i)) {
dot.style.opacity = '1';
}
container.appendChild(dot);
}
}
// 5. Handle Button Click Event
btn.addEventListener('click', () => {
btn.disabled = true;
resBox.classList.remove('show');
document.querySelectorAll('.player').forEach(p => p.classList.remove('active'));
d1Container.classList.add('shake');
d2Container.classList.add('shake');
audio.play();
setTimeout(() => {
d1Container.classList.remove('shake');
d2Container.classList.remove('shake');
const r1 = Math.floor(Math.random() * 6) + 1;
const r2 = Math.floor(Math.random() * 6) + 1;
createDice(d1Container, r1);
createDice(d2Container, r2);
if (r1 > r2) {
resBox.innerHTML = `Player 1 Wins! <i class="fa-solid fa-trophy"></i>`;
document.getElementById('player1').classList.add('active');
} else if (r2 > r1) {
resBox.innerHTML = `Player 2 Wins! <i class="fa-solid fa-trophy"></i>`;
document.getElementById('player2').classList.add('active');
} else {
resBox.innerHTML = `Its a Draw! <i class="fa-solid fa-handshake" ></i>`;
}
resBox.classList.add('show');
setTimeout(() => {
btn.disabled = false;
}, 1000);
}, 600);
});
// 6. Initialize
createDice(d1Container, 1);
createDice(d2Container, 1);
Detailed Code Explanation
Understanding how the pieces fit together is the most important part of learning web development. Let's break down the HTML, CSS concepts, and JavaScript logic used in our Dice Duel game.
1. Understanding the HTML Structure
The HTML acts as the skeleton of our game. Here is a breakdown of the key components:
-
The Result Box (
<div id="resultDisplay">): This is an empty container. We keep it empty in the HTML because the text inside (like "Player 1 Wins!", or "Draw!") will be dynamically injected by our JavaScript based on the dice roll results. -
The Player Cards:
We use a parent container
<div class="players">to hold both players. Inside, each player has an ID (#player1,#player2) so we can target them individually to apply the winning glow effect later. -
The Dice Containers (
<div class="dice">): These divs represent the physical square of the die. Inside these, JavaScript will generate<span>elements that act as the dots.
2. Key CSS Concepts Explained
While the CSS provides the colors and spacing, three specific layout and animation concepts make this project stand out:
-
CSS Grid for the Dice:
The
.diceclass usesdisplay: grid; grid-template-columns: repeat(3, 1fr);. This splits the dice square into a 3x3 grid (9 equal cells). This is the secret to placing the dots perfectly without complex positioning math. -
Flexbox for Layout:
The
bodyand.playerscontainer use Flexbox (display: flex) to center the game vertically and horizontally on the screen, and to keep the two player cards neatly side-by-side. -
CSS Animations:
We use
@keyframesto create ashakeDiceanimation. When a user clicks the button, JavaScript temporarily adds a.shakeclass to the dice, triggering this CSS animation to simulate physical rolling.
3. Decoding the JavaScript Logic
The JavaScript breathes life into the game. Here is a step-by-step explanation of what the script does:
-
DOM Selection:
The script starts by using
document.getElementById()to grab references to the HTML elements so we can manipulate them. -
The Audio API:
new Audio("URL")creates an audio object in the background. Callingaudio.play()triggers the sound effect the moment the button is clicked. -
The Dice Map (
diceLayouts): This is a crucial data structure. Since our dice is a 3x3 grid (numbered 0 to 8), we map out which cells need dots for each number. For example, a roll of "1" only needs a dot in cell 4 (the exact center). A roll of "2" needs dots in cells 0 (top-left) and 8 (bottom-right). -
The
createDice()Function: This function takes two arguments: the container to draw in, and the number rolled. It clears out any old dots, loops 9 times to create 9<span>elements, and checks ourdiceLayoutsmap to decide which spans should be visible (opacity: 1) and which should remain hidden. -
The Roll Logic (Event Listener): When the "Roll Dice"
button is clicked:
- The button is temporarily disabled to prevent double-clicking and breaking the animations.
- The
shakeclass is applied to the dice. -
A
setTimeout()is used to pause the core math logic for 600 milliseconds. This gives the CSS shake animation time to finish before the numbers change! -
Inside the timeout,
Math.random()generates two random numbers between 1 and 6. -
The
createDice()function is called to visually update the dice. -
An
if / else if / elsestatement compares the numbers to determine the winner, updates the#resultDisplaytext, and adds a CSS class to highlight the winning player's card.
Conclusion & Next Steps
Congratulations! You have built a dynamic, styled, and animated game mechanic from scratch. You've touched on responsive design, CSS Grid, animations, and DOM manipulation in JavaScript.
Want a challenge?
Try expanding on this project:
- Add a Score Tracker: Create variables for Player 1 and Player 2 scores and update them every time someone wins.
- Player Names: Add input fields allowing players to set their own names instead of "Player 1" and "Player 2".
Happy Coding! Share your versions in the comments over at TheVsHub.in!

Write a Comment