What we’re coding
Here’s the finished demo, to give you an idea of what we’re creating:
Begin with the UI
We’ll start by creating the interface. This will be a minimal design featuring the balance (how much credit there is to play with) and three reels.
The overall container comes first, so add a <div/>
element in your html file.
1 |
<div class="slot-machine"> |
2 |
|
3 |
<div/>
|
In a new <div/>
add an <h1/>
for the title and a <p>
to display the balance.
1 |
<div class="header"> |
2 |
<h1>Classic Slots</h1> |
3 |
<p class="balance">Balance: $1000</p> |
4 |
</div>
|
Arguably the most important part of the slot machine game is the reels. Reels are the vertical positions in a slot that spin around when the game starts. Add a <div/>
element to contain the reels.
1 |
<div class="reels-container"> |
2 |
|
3 |
<div /> |
The container will have three reels with each reel holding three symbols. We’ll use emojis for these, so use whatever you like:
1 |
<div class="reels-container"> |
2 |
<div class="reel"> |
3 |
<div class="symbol">🍒</div> |
4 |
<div class="symbol">🔔</div> |
5 |
<div class="symbol">🍋</div> |
6 |
</div>
|
7 |
<div class="reel"> |
8 |
<div class="symbol">🍒</div> |
9 |
<div class="symbol">🔔</div> |
10 |
<div class="symbol">🍋</div> |
11 |
</div>
|
12 |
<div class="reel"> |
13 |
<div class="symbol">🍒</div> |
14 |
<div class="symbol">🔔</div> |
15 |
<div class="symbol">🍋</div> |
16 |
</div>
|
17 |
</div>
|
Lastly, we will need the button to start the game and a GOOD LUCK message displayed with a <p/>
tag.
1 |
<div class="controls"> |
2 |
<button class="spin_btn">SPIN</button> |
3 |
<p class="message">Good Luck!</p> |
4 |
</div>
|
Styling the slot machine game
With the structure complete, let’s get styling it. For the stylish title we’ll need a custom font from Google Fonts, plus another for the messages.
In your CSS file , add this at the top.
1 |
import url("https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"); |
2 |
@import url('https://fonts.googleapis.com/css2?family=Ultra&display=swap'); |
Next, use flex to align everything to the center, horizontally and vertically. Also, add the universal font and a background color.
1 |
body { |
2 |
background: #121212; |
3 |
display: flex; |
4 |
justify-content: center; |
5 |
align-items: center; |
6 |
height: 100vh; |
7 |
font-family: "DM Mono", monospace; |
8 |
}
|
Add these additional styles to ensure the header contents are appropriately styled and all the content is aligned.
1 |
.slot-machine { |
2 |
text-align: center; |
3 |
}
|
4 |
.header { |
5 |
margin-bottom: 20px; |
6 |
padding: 10px; |
7 |
}
|
8 |
.header h1 { |
9 |
font-family: "Ultra", serif; |
10 |
font-size: 6em; |
11 |
line-height: .9; |
12 |
margin: 0; |
13 |
color: #ebb701; |
14 |
max-width: 10ch; |
15 |
}
|
16 |
.header p { |
17 |
font-size: 1.2em; |
18 |
margin-top: 10px; |
19 |
color: white; |
20 |
}
|
So far we have this:
The next step is to add styles to the reels to make three columns:
1 |
.reels-container { |
2 |
display: flex; |
3 |
justify-content: center; |
4 |
align-items: center; |
5 |
height: 60%; |
6 |
}
|
7 |
.reel { |
8 |
display: block; |
9 |
width: 130px; |
10 |
height: 210px; |
11 |
background-color: #3a3a3a; |
12 |
border-radius: 15px; |
13 |
overflow: hidden; |
14 |
margin: 0 10px; |
15 |
box-shadow: 0px 14px 15px -5px rgba(0,0,0,0.3) inset, |
16 |
1px -14px 15px -5px rgba(0,0,0,0.3) inset; |
17 |
border: 2px solid rgba(255,255,255,0.2); |
18 |
position: relative; |
19 |
}
|
In the styles above, we have added display:flex
on the reels-container which ensures the reels are aligned by columns. Additionally, to center the reels, we have used justify-content: center;
and align-items: center;
.
For each reel, we have a custom width and height and a few other properties such as box-shadow,
background-color,
and border-radius
to make it more appealing.
We have also added some margin
to ensure the reels are spaced in the container.
The symbols will have the following styles.
1 |
.symbol { |
2 |
font-size: 3em; |
3 |
height: 1.5em; |
4 |
opacity: 0.6; |
5 |
}
|
Lastly, add these styles for the SPIN button and the element containing the GOOD LUCK message.
1 |
.controls .btn { |
2 |
margin: 3em 0 1em 0; |
3 |
}
|
4 |
.controls .message { |
5 |
font-size: 1.5em; |
6 |
color: #ebb701; |
7 |
}
|
Get the elements with the DOM
Get all the DOM elements that will need manipulation. At the top of your JS file add this code.
1 |
const reels = document.querySelectorAll(".reel"); |
2 |
const spinButton = document.querySelector(".spin_btn"); |
3 |
const messageDisplay = document.querySelector(".message"); |
4 |
const reelSound = document.getElementById("reelSound"); |
5 |
const winSound = document.getElementById("winSound"); |
The probability of winning when playing a slot machine can be very low. To increase the chances of winning, we are going to create an array of reel states which look like this:
1 |
let reelStates = [ |
2 |
["🍒", "🍒", "🍒", "🍋", "🍋", "🍉"], |
3 |
["🍒", "🍋", "🍋", "🍉", "🍒", "7️⃣"], |
4 |
["🍒", "7️⃣", "🍋", "🍒", "🍓", "🍋"] |
5 |
];
|
From the array, you can see that some symbols appear more than others, this is intentional to increase the probability of winning. Each time a player wants to play this game, they will have an initial balance of $ 1000 and whenever they spin the slot machine, a value of $10 will be deducted from their balance.
Define these initial variables.
1 |
let spinning = false; |
2 |
let balance = 1000; |
3 |
let betAmount = 10; |
We have also added a variable spinning
which will keep track of whether the game is spinning or not. The initial value will be set to false (i.e. not spinning).
To update the remaining balance, create a function called updateBalanceDisplay()
and add the code below. This function will be called after every spin.
1 |
function updateBalanceDisplay() { |
2 |
const balanceDisplay = document.querySelector(".balance"); |
3 |
balanceDisplay.textContent = `Balance: $${balance}`; |
4 |
}
|
To place a bet or spin the game, we first need to check if the balance is adequate, i.e. balance should be more than the bet amount. If the balance is more than the bet amount, the player will have an opportunity to continue playing. If not we will display the message “Not enough balance to place a bet!”.
Create a function called placeBet()
and add this code:
1 |
function placeBet() { |
2 |
if (balance >= betAmount) { |
3 |
balance -= betAmount; |
4 |
updateBalanceDisplay(); |
5 |
|
6 |
} else { |
7 |
alert("Not enough balance to place a bet!"); |
8 |
}
|
9 |
}
|
Attach an event listener to the spin button, so that when the user clicks the button, it will trigger the placeBet()
function. The placeBet()
function will update the balance and manage spinning and winning logic.
For now, the placeBet()
function updates the balance only, we will update the rest of the logic later.
Spin the reels
The most important feature of the slot machine is the ability to spin reels. Currently, we have three columns with each column containing 3 symbols. The spinning logic will involve injecting new symbols into each reel, when the spinning starts. A win will be determined by matching three symbols in the same row.
To determine the cycle spin i.e. how many times the reel will spin before it comes to a halt, we will use this formula.
1 |
const spinCount = 10 + Math.floor(Math.random() * 5); |
Create a new function called spinReel
and add the formula at the top of the function. This function will take the reel
and index
as parameters.
1 |
function spinReel(reel, index) { |
2 |
const spinCount = 10 + Math.floor(Math.random() * 5); |
3 |
|
4 |
}
|
To determine the starting point, we are starting from 0, This value will increase by 1 until the value of spinCount
is reached. Initialize the initial value using currentSpin
and set it to 0. This value will increment with each spin until we get to the value of spinCount
.
1 |
function spinReel(reel, index) { |
2 |
const spinCount = 10 + Math.floor(Math.random() * 5); |
3 |
let currentSpin = 0; |
4 |
}
|
To ensure the reels spin at different intervals, we will use the setInterval()
function, which executes a specified function repeatedly after a given time interval.
To achieve a spinning effect, we will get each array from realStates
(a dimensional array) and interchange the positions of the elements. At the same time, the elements will be added to the DOM hence creating a spinning illusion.
Create a function named spinReel
, add the spinCount
variable, initialize currentSpin
to 0, and create an interval that will run every 50 milliseconds.
1 |
function spinReel(reel, index) { |
2 |
const spinCount = 10 + Math.floor(Math.random() * 5); |
3 |
let currentSpin = 0; |
4 |
console.log(spinCount); |
5 |
const interval = setInterval(() => {}, 50 + index * 50); |
6 |
}
|
Each array in the reelStates
array contains 6 symbols. To create the illusion of spinning, we will take the last item in the array and move it to the beginning (i.e., rotate the last element to the front).
After changing the order of the symbols in the array, they will be added to the DOM to effect the rotation.This sudden change in the position of the symbols will create the visual effect of spinning.
Update the code inside the setInterval()
function as shown below.
1 |
const interval = setInterval(() => { |
2 |
console.log(reelStates[index]); |
3 |
reelStates[0].unshift(reelStates[0].pop()); |
4 |
reel.innerHTML = ""; |
5 |
reelStates[index].forEach((symbol) => { |
6 |
const symbolDiv = document.createElement("div"); |
7 |
symbolDiv.classList.add("symbol"); |
8 |
symbolDiv.textContent = symbol; |
9 |
reel.appendChild(symbolDiv); |
10 |
});
|
11 |
currentSpin++; |
12 |
|
13 |
}, 50 + index * 50); |
14 |
}
|
Let’s breakdown the code above: For example for index =0:
-
reelstates[index].pop()
removes the last element in the first array( [“🍒”, “🍒”, “🍒”, “🔔”, “🍋”, “🍉”] ) and returns it. The last element in this case is “🍉. Now reelStates[index] has only 5 items. -
reelStates[index].unshift()
adds the element returned (“🍉) back to the array at the beginning of the array. This process of removing and adding elements to the array will occur every 50 milliseconds plus an additional stagger value based on the reel’s index. - After each array is updated we also update the DOM to simulate the spinning effect. Then we increment the spinning value creating an infinite spinning.
At some point, we need to stop the spinning so that a win or loss happens. To do that we will check if the current spin value is greater than or equal to the spin count and if so, we will clear the interval. Clearing the interval means that the reels will stop spinning.
Update the code as follows:
1 |
function spinReel(reel, index) { |
2 |
const spinCount = 10 + Math.floor(Math.random() * 5); |
3 |
let currentSpin = 0; |
4 |
console.log(spinCount); |
5 |
const interval = setInterval(() => { |
6 |
reelStates[index].unshift(reelStates[index].pop()); |
7 |
reel.innerHTML = ""; |
8 |
reelStates[index].forEach((symbol) => { |
9 |
const symbolDiv = document.createElement("div"); |
10 |
symbolDiv.classList.add("symbol"); |
11 |
symbolDiv.textContent = symbol; |
12 |
reel.appendChild(symbolDiv); |
13 |
});
|
14 |
currentSpin++; |
15 |
if (currentSpin >= spinCount) { |
16 |
clearInterval(interval); |
17 |
if (index === reels.length - 1) { |
18 |
spinning = false; |
19 |
|
20 |
|
21 |
}
|
22 |
}
|
23 |
}, 50 + index * 50); |
24 |
}
|
The spinReel()
function will only work for one reel. Rather than invoking the function three times, create a function named spinReels
which will spin all the reels.
1 |
function spinReels() { |
2 |
if (spinning) return; |
3 |
spinning = true; |
4 |
reelSound.play(); |
5 |
messageDisplay.textContent = "Spinning........."; |
6 |
reels.forEach((reel, index) => { |
7 |
spinReel(reel, index); |
8 |
});
|
9 |
}
|
In this function, we first check whether the reels are already spinning with the spinning variable. If the reels are spinning, no further action will occur and the function will return at the point. If the reels are spinning, we set the spinning variable to true, display a message, and spin all the reels while the reelSound
sound is playing giving an indicator that the reels are spinning.
In the placeBet()
function, call the spinReels()
function so that when the button is clicked, the reels will start spinning.
1 |
function placeBet() { |
2 |
if (balance >= betAmount) { |
3 |
balance -= betAmount; |
4 |
updateBalanceDisplay(); |
5 |
spinReels(); |
6 |
} else { |
7 |
alert("Not enough balance to place a bet!"); |
8 |
}
|
9 |
}
|
Check for a win
In a slot machine, the game is considered a win if a player matches the symbols in a row. Let’s implement this logic. After the reels stop, we will get the currently displayed symbols for the first 2 rows, then check if there is a matching pair across the board. If a match is found we will update the balance and play the winning sound.
However, if no matching symbols in a row occur, we will display a message promoting the user to try again.
1 |
function checkWin() { |
2 |
const [reel1, reel2, reel3] = reelStates.map((reel) => reel[0]); |
3 |
const [reel4, reel5, reel6] = reelStates.map((reel) => reel[1]); |
4 |
|
5 |
if ( |
6 |
(reel1 === reel2 && reel2 === reel3) || |
7 |
(reel4 === reel5 && reel5 === reel6) |
8 |
) { |
9 |
const payout = betAmount * 5; |
10 |
balance += payout; |
11 |
console.log("win"); |
12 |
winSound.play(); |
13 |
messageDisplay.textContent = " "; |
14 |
} else { |
15 |
messageDisplay.textContent = "Try Again"; |
16 |
}
|
17 |
updateBalanceDisplay(); |
18 |
}
|
Update the spinReel()
function so the checkWin()
function is called after the reels stop spinning.
1 |
function spinReel(reel, index) { |
2 |
//......
|
3 |
if (currentSpin >= spinCount) { |
4 |
clearInterval(interval); |
5 |
if (index === reels.length - 1) { |
6 |
spinning = false; |
7 |
reelSound.pause(); |
8 |
reelSound.currentTime = 0; |
9 |
checkWin(); |
10 |
}
|
11 |
}
|
12 |
//........
|
13 |
}
|
And we’re done! Here’s what we’ve built:
Next steps
To make the slot machine app even better, consider adding a scoring tracker to keep track of the highest score. You can also add animations for a more engaging user experience. Enjoy!