🎁 Give the #1 gift request of 2024... a ZTM membership gift card! 🎁

Build A JavaScript Game Step-By-Step (Using HTML, CSS + JavaScript)

Jacinto Wong
Jacinto Wong
hero image

Want to improve your JavaScript skills, while also showing your appreciation for the Big Bang Theory?

Well, good news! In this tutorial, I’ll walk you through how to code and create a more complex version of the classic rock-paper-scissors game i.e. rock, paper, scissors, lizard, spock!

rock paper scissors lizard spock game

I've got your back with the CSS, including the media queries for seamless responsive design on smartphones. This way, we can dive right into the game's functionality.

rock paper scissors lizard spock game finished in javascript

Our game will feature dynamic elements, such as:

  • Executing turns with a single click on an icon
  • Automatic computer moves with random selections
  • Score tracking for player and computer
  • A results message at the bottom
  • Confetti falling when the player wins a round, and
  • Game reset and restart by clicking on an icon

Moreover, we'll explore JavaScript modules and import confetti functions from a separate file.

You can find the code to work on this project here:

Quick side note before we dive in

If you stumbled upon this page simply due to your love of the Big Bang Theory and want to learn to code (or more likely you're having some trouble learning to code), then I highly recommend you check out Andrei’s Coding Bootcamp course.

I personally learned to code from Andrei many years ago using his Coding Bootcamp (Complete Web Developer). Thanks to that, I've been a Developer for 5+ years now and even become an instructor myself as well.

It all started from taking Andrei's Coding Bootcamp and I'm just one of 1,000s of students that have a similar story to mine.

So if you want to learn to code and actually make a career out of it, there is still no better place in my mind!

learn web development

Andrei didn't even ask me to say any of this but I am anyways because I have been in your shoes as a complete beginner.

It makes sense that it's one of the most popular and highly-rated coding bootcamps online. He is constantly updating it (more than any other coding bootcamp I've seen), he goes way above and beyond, and actually makes learning fun.

And web development is a great career and not just because the money is good.

... but the money is good: The average salary for a Full-Stack Web Developer at the moment is around $117,800 in the US.

full-stack web dev salary 2024

Not bad eh!?

Alternatively, if you’re already an aspiring Developer or Junior Developer looking to improve your skills by building some JavaScript projects (that you can also add to your portfolio), then I'd love to help you out.

The project we're going to walk through in this tutorial is just 1 of the 20 projects you'll get to build in my JavaScript portfolio projects course.

If you find yourself getting stuck somewhere in this tutorial, then it might be helpful for you to see me building the project step-by-step alongside you (plus you'll be able to ask me questions directly on Discord).

advanced javascript portfolio projects

Oh, and FYI: If you join Zero To Mastery, you get access to all of the courses on ZTM, not just the two I've mentioned.

Alright, with that out of the way, let’s get building this JavaScript game project shall we?

Step 1: Building the User Interface (UI) with HTML

First things first, there is a template for this project that will make it easier to follow along.

You can get it here.

Open it in Visual Studio Code (VSC) and launch it with a live server.

Creating the HTML structure

Now that you have the project template, let's start building our HTML:

 <div class="game-container">
    <!-- Header -->
        <div class="header">
            <h1>Rock Paper Scissors Lizard Spock</h1>
        </div>
<!-- Player Container -->
        <div class="player-container" id="player">
<h2>You - <span id="playerScore">0</span><span class="choice" id="playerChoice"> --- Choice</span></h2>

Get icons

Next, we need to grab the icons for the game images. I recommend you use FontAwesome icons.

get icons

They have all the necessary icons to create this game, as seen below:

RECOMMENDED ICONS

We'll then use these icons as buttons to select as our choice in each match.

Create the player container

Start by adding your icons in the player container. (If you use other icons, be sure to reflect that here).

<i class="far fa-hand-rock" title="Rock" id="playerRock"></i>
<i class="far fa-hand-paper" title="Paper" id="playerPaper"></i>
<i class="far fa-hand-scissors" title="Scissors" id="playerScissors"></i>
<i class="far fa-hand-lizard" title="Lizard" id="playerLizard" ></i>
<i class="far fa-hand-spock" title="Spock" id="playerSpock"></i>
        </div>

Create the computer container

Next, we need to make a computer container for us to play against, so go ahead and duplicate the player container for the computer container, as the code is almost identical.

You just need to replace all instances of 'player' with 'computer', but you can do that with the multi-select tool:

<!-- Computer Container -->
        <div class="player-container" id="computer">
            <h2>Computer - <span id="computerScore">0</span><span class="choice" id="computerChoice"> --- Choice</span></h2>
<i class="far fa-hand-rock" title="Rock" id="computerRock"></i>
<i class="far fa-hand-paper" title="Paper" id="computerPaper"></i>
<i class="far fa-hand-scissors" title="Scissors" id="computerScissors"></i>
<i class="far fa-hand-lizard" title="Lizard" id="computerLizard"></i>
<i class="far fa-hand-spock" title="Spock" id="computerSpock"></i>
        </div>

Reset icon and results container

Lastly, add a reset icon and a container to display the results:

<!-- Reset Icon -->
        <i class="fas fa-sync-alt reset-icon" title="Reset"></i>
<!-- Results Text -->
        <div class="result-container">
            <h3 class="result-text" id="resultText">You Won!</h3>
        </div>
    </div>

Save your file and check the progress. It should end up looking like this:

initial build

Not bad right? It needs some improvements, but we can refine these in our CSS.

Step 2: Refining the User Interface (UI) with CSS

I've included most of the CSS for this tutorial in the project template, but let’s go over a few key aspects of how we can improve, what we’re going to refine, and why.

Improve the body background for better visibility

Let's start with the body background. We want it to be slightly gray so that it stands out on white screen. Right now it kinds of blends:

BLEND

So go ahead and add a color to help it pop, like so:

background: whitesmoke;

In theory, you could use any color you want.

Edit the game container UI

Next, let's style the game container itself, and give it some shading, so it pops even more, like so:

pop

Not bad right? We can edit it with the following code:

.game-container {
  width: 530px;
  height: 600px;
  background: white;
  box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.5);
  border-radius: 5px;
}

The background looks good, but the layout is a little bit of a mess.

With that in mind, let’s fix the header div and the icons' layout. Additionally, we'll check the mobile responsiveness of our container.

Edit the Header styling

Let’s start by adjusting the height and border radius of the header, as it’s currently not rounded, like the bottom of the body.

 border-top-left-radius: 5px;
  border-top-right-radius: 5px;
  height: 100px;

Save and check, and it looks much better.

add a rounded header

Check mobile responsiveness

Now, let's examine the smartphone vertical view.

CHECK THE INTIAL MOBILE VIEW

It’s running right up to the edge of the screen. Ideally, we want the margins on the left and right to display the shadow.

To fix this, at the bottom of our CSS, I've added several media queries, to update the game container width:

.game-container {
    width: 95%;

Save and check.

mobile view with margins

Fantastic. Now it looks great on both large smartphones and iPhones.

Edit the icon's color and highlight the selection

Ok, let’s do 3 things to improve the icons:

  1. Change the icon's initial color
  2. Add an indicator to help identify which option is being selected
  3. Also, let's add different colors for the player and computer

We can do this like so...

Adding player and computer icon colors to help differentiate

#player .far,
#player .choice {
  color: dodgerblue;
  cursor: pointer;
}

#computer .far,
#computer .choice {
  color: rgb(235, 43, 52);
}

Save and check.

added icon colours

The splash of color looks fantastic!

Test adding a selection identifier

Next, we'll add a selected class, for when one of our icons is clicked. Let's test it by adding the class manually to our paper element in the HTML:

.selected {
  color: black;
}

Use !important to override the color property in this case:

.selected {
  color: black !important;
}

Save and check to see if it works!

ICON turns black

Now that you know it works, go ahead and remove the !important override. We’ll add the functionality in just a second, but we have some final tweaks to do first.

Edit the icon layout

Currently, there’s a large margin on the right-hand side of the icons, so let’s remove the margin on the last icon in each row:

#player .far:last-of-type,
#computer .far:last-of-type {
  margin-right: 0;
}
working as intended

We're done with our styling so now it's time to move on to functionality!

Step 3: JavaScript - Player Selection

We know that the highlighted icon should turn black, however, we haven’t added the functionality for that to work just yet, so let’s do it now.

First, create the constants for the HTML elements that we want to reference:

const playerScoreEl = document.getElementById('playerScore');
const playerChoiceEl = document.getElementById('playerChoice');
const computerScoreEl = document.getElementById('computerScore');
const computerChoiceEl = document.getElementById('computerChoice');
const resultText = document.getElementById('resultText');

const playerRock = document.getElementById('playerRock');
const playerPaper = document.getElementById('playerPaper');
const playerScissors = document.getElementById('playerScissors');
const playerLizard = document.getElementById('playerLizard');
const playerSpock = document.getElementById('playerSpock');

const computerRock = document.getElementById('computerRock');
const computerPaper = document.getElementById('computerPaper');
const computerScissors = document.getElementById('computerScissors');
const computerLizard = document.getElementById('computerLizard');
const computerSpock = document.getElementById('computerSpock');

const allGameIcons = document.querySelectorAll('.far');

Next, create the selection logic for the player icons by adding an onclick attribute to the HTML:

onclick="select('rock')"
X5

Now, create the select function in JavaScript:

// Passing player selection value and styling icons
function select(playerChoice) {
  console.log(playerChoice);
}

When we save and check the console, we can see the value is being passed through.

select function

When we click on an icon, it’s being selected.

Now, let's use this to figure out our selection logic for our CSS, as we want the clicked icon to turn black and update the choice text as well.

Remove the console log and use a switch statement:

 // Add 'selected' styling & playerChoice
  switch (playerChoice) {
    case 'rock':
      playerRock.classList.add('selected');
      playerChoiceEl.textContent = ' --- Rock';
      break;
    case 'paper':
      playerPaper.classList.add('selected');
      playerChoiceEl.textContent = ' --- Paper';
      break;
    case 'scissors':
      playerScissors.classList.add('selected');
      playerChoiceEl.textContent = ' --- Scissors';
      break;
    case 'lizard':
      playerLizard.classList.add('selected');
      playerChoiceEl.textContent = ' --- Lizard';
      break;
    case 'spock':
      playerSpock.classList.add('selected');
      playerChoiceEl.textContent = ' --- Spock';
      break;
    default:
      break;
  }

Save and check.

ICON turns black

It works, but still needs a little work.

Why? Well, updating the text selection choice works fine, but once we select an icon, it stays selected permanently, which is not great.

selection stays locked

We want it to change between each new selection, as well as keep updating the text (which currently works).

Adding a resetSelected function

We can solve this by adding another function to reset the icons after each selection. We'll use the allGameIcons array for this.

Start by creating the resetSelected function:

// Reset all 'selected' icons, remove confetti
function resetSelected() {
  allGameIcons.forEach((icon) => {
    icon.classList.remove('selected');
  });
}

Then, call resetSelected from the select function, before assigning the selected class:

function select(playerChoice) {
  resetSelected();

Save and check to see if it works.

working as intended

The player selection is fully functional now, so in the next step, we'll focus on the computer-related functionality.

Step 4: JavaScript - Computer Selection

In this section, we'll create logic for the computer to make a random selection and display the selection like how we're displaying the player selection.

First, create a new function named checkResult which will call other functions:

function select(playerChoice) {
  checkResult();
// Call functions to process turn
function checkResult() {
  resetSelected();
  computerRandomChoice();

Then, create a variable at the top for containing a string of the computerChoice:

let computerChoice = '';

Now, use a Math method called random to get a random number. We will divide this into 5 ranges and select based on that:

// Random computer choice
function computerRandomChoice() {
  const computerChoiceNumber = Math.random();
  console.log(computerChoiceNumber);

Save and check.

random numbers working

Now, use a series of if statements to change the computerChoice value, like so:

 if (computerChoiceNumber < 0.2) {
    computerChoice = 'rock';
  } else if (computerChoiceNumber <= 0.4) {
    computerChoice = 'paper';
  } else if (computerChoiceNumber <= 0.6) {
    computerChoice = 'scissors';
  } else if (computerChoiceNumber <= 0.8) {
    computerChoice = 'lizard';
  } else {
    computerChoice = 'spock';
  }
  console.log(computerChoice);

Save and check.

random computer choice

Next, reuse the same functionality from the player select function and create a new function called displayComputerChoice:

// Add 'selected' styling & computerChoice
function displayComputerChoice() {
  switch (computerChoice) {
    case 'rock':
      computerRock.classList.add('selected');
      computerChoiceEl.textContent = ' --- Rock';
      break;
    case 'paper':
      computerPaper.classList.add('selected');
      computerChoiceEl.textContent = ' --- Paper';
      break;
    case 'scissors':
      computerScissors.classList.add('selected');
      computerChoiceEl.textContent = ' --- Scissors';
      break;
    case 'lizard':
      computerLizard.classList.add('selected');
      computerChoiceEl.textContent = ' --- Lizard';
      break;
    case 'spock':
      computerSpock.classList.add('selected');
      computerChoiceEl.textContent = ' --- Spock';
      break;
    default:
      Break;

However, this won’t work on its own.

We also need to call the displayComputerChoice function in the checkResult function:

// Call functions to process turn
function checkResult() {
  resetSelected();
  computerRandomChoice();
  displayComputerChoice();

Save and check, and it should look like this:

WE WON

The computer selection is now working! In the next step, we will implement the logic of the game.

Step 5: JavaScript - Check Results

So far, we’ve created a working project that follows the rules for a standard game of rock, paper, scissors. However, the rules are different in rock, paper, scissors, lizard, spock, so we need to adjust this in the code.

As a quick recap:

  • Scissors cut Paper
  • Paper covers Rock
  • Rock crushes Scissors

But now we also have:

  • Rock crushes Lizard
  • Scissors decapitates Lizard
  • Lizard eats Paper
  • Lizard poisons Spock
  • Paper disproves Spock
  • Spock smashes Scissors
  • Spock vaporizes Rock

Phew!

Fortunately, it’s not too complex to code these additional rules, and in this section, we'll create the logic of the game itself.

So let’s work through this.

We have an object with each possible choice, containing its own object that has an array of the two choices it defeats:

const choices = {
  rock: { defeats: ['scissors', 'lizard'] },
  paper: { defeats: ['rock', 'spock'] },
  scissors: { defeats: ['paper', 'lizard'] },
  lizard: { defeats: ['paper', 'spock'] },
  spock: { defeats: ['scissors', 'rock'] },
};

Create global variables for the score:

let playerScoreNumber = 0;
let computerScoreNumber = 0;

Then update the select and checkResult functions to pass the playerChoice as a parameter:

function select(playerChoice) {
  checkResult(playerChoice);
function checkResult(playerChoice) {
...
  updateScore(playerChoice);

Then go ahead and create the updateScore function:

// Check result, increase scores, update resultText
function updateScore(playerChoice) {
  console.log(playerChoice, computerChoice);

Add in the ability to check for a tie:

 if (playerChoice === computerChoice) {
    resultText.textContent = "It's a tie.";
  }

And then check to see if it works…

tie function works

Bazinga!

Now to the real logic. We’ll create an else statement if we are not tied.

Within that statement, we will create a choice constant, which will call an item from the choices array, matching the playerChoice to a key name:

  } else {
    const choice = choices[playerChoice];
    console.log(choice);

First, we will modify our console log and see how it works.


   console.log(choice.defeats.indexOf(computerChoice));

Save and check.

array works

As you can see in the example, we chose rock. The array then clarifies that rock can beat both scissors and lizard. Because the computer chose scissors, we won!

Go ahead and play a few rounds to check that it all works, and keep an eye on the code as you do this.

Notice that when the player wins the value is 0 or 1, which means that the computer choice was found in the defeats array as the first or second item.

If it is not in the array, it returns -1.

winning value

So, now, we know that if the number is greater than -1, then the player has won.

Let’s add in the logic to display this.

   if (choice.defeats.indexOf(computerChoice) > -1) {
      resultText.textContent = 'You Won!';
      playerScoreNumber++;
      playerScoreEl.textContent = playerScoreNumber;

We already know that when the value is 0 or 1 , then the player wins, the score goes up and the message says “You won”.

However, when the computer wins nothing happens, so let’s fix that, and make it say “You lost” when the player loses.

We just need to add an else statement and mirror what we were doing for updating our text and the score.

  } else {
      resultText.textContent = 'You Lost!';
      computerScoreNumber++;
      computerScoreEl.textContent = computerScoreNumber;
    }

Simple!

The game logic is now complete! In the next step, we will add the ability to reset everything and start over.

Step 6: JavaScript - Reset All

In this section, we'll create a function to reset everything and start over. This will include resetting the score, player and computer choices, and selected icons.

First, let's clean up the HTML by removing any unnecessary text that was initially hardcoded, since our JavaScript is now populating the content.

Create the resetAll function

This function will:

  • Reset the player and computer scores
  • Update the score elements in the user interface
  • Clear the player and computer choice text
  • Clear the result text, and
  • Reset the selected icons by calling the resetSelected function

It looks like this:

// Reset score & playerChoice/computerChoice
function resetAll() {
  playerScoreNumber = 0;
  computerScoreNumber = 0;
  playerScoreEl.textContent = playerScoreNumber;
  computerScoreEl.textContent = computerScoreNumber;
  playerChoiceEl.textContent = '';
  computerChoiceEl.textContent = '';
  resultText.textContent = '';
  resetSelected();
}

In the HTML file, add the onclick attribute for the resetAll function to the "Reset" button:

<!-- Reset Icon -->
<i class="fas fa-sync-alt reset-icon" title="Reset" onclick="resetAll()"> </i>

Since we want the initial score values to be displayed when the page loads, call the resetAll function on startup:

// On startup, set initial values
resetAll();

Save your changes and check the result in your browser.

The reset functionality should now be working as expected. When you click the "Reset" button, the scores, choices, and selected icons will reset to their initial state.

By following these steps, you've added a reset function to the game. In the next step, we'll work on making winning a round a little more exciting.

Step 7: JavaScript - Confetti Functionality

In this section, we'll add confetti functionality to the game.

Basically, when the player wins a round, the confetti show. Then, it will stop immediately if the next round is a tie or if the player loses.

We'll use an external script for the confetti effect, so go to the following link and download the confetti.js file.

Unzip the downloaded file, then copy and paste the confetti.js file into your project folder. Then, go ahead and open the file in your code editor (e.g., Visual Studio Code).

// Confetti.js - downloaded from https://www.cssscript.com/confetti-falling-animation/

Copy the entire content of confetti.js and paste it at the bottom of your main JavaScript file.

We will need to modify it slightly, as we want to be able to call the functions, so we will remove the function wrapping everything, IIFE (immediately-invoked function expression)

To test the confetti effect, call the startConfetti function when the page loads:

startConfetti();

Save your changes and check the result in your browser.

The confetti effect should look something like this:

confetti

Adjust the confetti settings according to your preference.

For example

You can change the maxCount to 100 and the animationSpeed to 10.

Now, we need to call the startConfetti, stopConfetti, and removeConfetti functions at the appropriate places in our code.

First, remove the startConfetti function call at the bottom of the script.

Then, call the startConfetti function when the player wins a round, by adding it inside the if statement that checks if the player has won:

  if (choice.defeats.indexOf(computerChoice) > -1) {
      startConfetti();

Call the stopConfetti and removeConfetti functions inside the resetSelected function to stop the confetti effect every time the user selects an icon:


function resetSelected() {
...
  stopConfetti();
  removeConfetti();

Save your changes and test the result in your browser. The confetti effect should start when the player wins and stop instantly when the next round begins.

You've now added confetti functionality to the game. In the next step, you can refactor the code and learn how to implement modules in JavaScript for better organization and separation of concerns.

Step 8: JavaScript - Module Refactoring

Recommended reading references:

In this step, we'll refactor our project to use ES Modules for better organization and separation of concerns. This will involve making changes to the script.js, confetti.js, and index.html files.

First, cut the confetti-related code from your script.js file and paste it into the confetti.js file.

Then, at the bottom of the confetti.js file, add the following line to export the confetti functions:

export { startConfetti, stopConfetti, removeConfetti };

In the script.js file, import the confetti functions at the top:

import { startConfetti, stopConfetti, removeConfetti } from './confetti.js';

In the index.html file, add the type attribute of module to the script.js:

    <script src="script.js" type="module"></script>

Alright so let’s see if this works.

Test the confetti functionality by calling the startConfetti function at the bottom of the script.js file:

startConfetti();

Save your changes and check the result in your browser. The confetti effect should still start as expected.

confetti

Once you can see that it works, let’s tweak this.

Go ahead and remove the startConfetti function call from the bottom of the script.js file. Then save your changes and try selecting an icon in your browser.

The confetti effect may not work as expected due to the module's scope. To fix this, you can pass the functions from the script.js file to the global window object.

Below your two functions (resetAll and select), add the following lines:

window.resetAll = resetAll;
window.select = select;

Save your changes and test the result in your browser, and everything should work as expected.

Congratulations! You have successfully refactored your project to use ES Modules. This enhances the organization and separation of concerns in your code.

Did you get it built?

So there you have it. Hopefully, you’ve been following along and building as you read through - if not, then get it built now and improve that portfolio to prep for that dev interview!

Although this is a fairly fun project, we’ve actually covered a lot of different topics here.

  • We've built from scratch using HTML, CSS, and JavaScript
  • We've covered various aspects of web development, including structuring HTML elements, styling with CSS, handling user interactions, and implementing game logic with JavaScript
  • We added confetti animation for a more engaging user experience
  • And we refactored the project to use ES Modules for better organization and separation of concerns!

No joke - By completing this tutorial, you've gained valuable skills in web development and deepened your understanding of how HTML, CSS, and JavaScript work together to create dynamic and interactive web applications. You can now take these skills and apply them to your own projects or continue learning and exploring more advanced topics in web development.

If you want more projects like this, be sure to check out my 20 Advanced JavaScript Projects course.

Or, if you struggled with building this game, and want a deep dive back into JavaScript, go ahead and check out ZTM’s Complete Web Developer course.

You can watch the first few videos here for free!

More from Zero To Mastery

[Guide] Computer Science For Beginners preview
[Guide] Computer Science For Beginners

You DO NOT need a CS Degree to get hired as a Developer. Learn Computer Sciences Basics today with this free guide by a Senior Dev with 10+ years of experience.

Top 9 Beginner JavaScript Practice Projects (Yes, Code Included!) preview
Top 9 Beginner JavaScript Practice Projects (Yes, Code Included!)

Hey Web Dev - do you want to sharpen your JavaScript skills? Then come join me and let's start building some awesome beginner friendly JavaScript projects to boost your portfolio & skills.

Beginners Guide to Animations in JavaScript (With Code Examples!) preview
Beginners Guide to Animations in JavaScript (With Code Examples!)

Want to add some life to your web projects? In this tutorial, you'll learn how to code animations using CSS and JavaScript (including anime.js & three.js).