Who doesn’t love a good memory game? They’re fun, fun, and a fun way to give your brain some exercise. But what if you could not only play one, but build one yourself? That’s exactly what we’re going to do in today’s tutorial.
Building a JavaScript memory game in HTML, CSS, and JavaScript is a good project for beginners and a great way to grasp front-end web development. By the end of this article, you will have a working memory game and a fuller understanding of how these essential web technologies work together.

Why Build a JavaScript Memory Game?
So why bother creating a memory game? Well, it will be fun, but it also has so many more benefits for new developers:
HTML Structure: You will learn about semantically structuring your game using HTML elements.
CSS Styling: You will practice working with CSS to ensure your game is visually appealing and responsive.
JavaScript Logic: This is where the fun begins! You will learn how to handle events, manipulate arrays, use conditionals, and utilise timing functions – all critical JavaScript concepts.
Problem Solving: You will experience many of the common problems you may face, which and allow you to improve your problem-solving skills.
Portfolio Project: You will create a working memory game that will be a valuable addition to your portfolio and provide evidence of your front-end development capabilities.
Flow Diagram

Getting Started: The Basic Setup
Before we write the code, let’s set up our project files. You’ll need three files:
index.html
(for the game structure)style.css
(for the game’s appearance)script.js
(for the game’s logic)
Create a new folder for your project and place these three empty files inside it.
Step 1: HTML Structure (index.html
)
Our HTML will provide the basic layout for our game. We’ll need a container for our game board and a place to display the score or status.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Memory Game</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="game-container">
<h1>Memory Game</h1>
<div class="score-display">Score: <span id="score">0</span></div>
<div class="game-board" id="gameBoard"></div>
<button id="resetButton">Reset Game</button>
</div>
<script src="script.js"></script>
</body>
</html>
Explanation:
- We link our
style.css
file in the<head>
. - Have a main
game-container
to hold everything. - A
score-display
to show the player’s score. - The
game-board
div is where our memory cards will be dynamically generated by JavaScript. - A
ResetButton
to start a new game. - Link our
script.js
file just before the closing</body>
tag. This ensures the HTML is loaded before the JavaScript tries to manipulate it.
Step 2: CSS Styling (style.css
)
Now let’s make our game look good! We’ll add some basic styling to our containers and the memory cards themselves.
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f0f0;
margin: 0;
}
.game-container {
background-color: #fff;
padding: 30px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
}
h1 {
color: #333;
margin-bottom: 20px;
}
.score-display {
font-size: 1.2em;
margin-bottom: 20px;
}
.game-board {
display: grid;
grid-template-columns: repeat(4, 100px); /* Adjust as needed for more cards */
grid-gap: 10px;
perspective: 1000px; /* For flip animation */
margin: 0 auto 20px auto;
}
.card {
width: 100px;
height: 100px;
background-color: #ccc;
border-radius: 8px;
cursor: pointer;
position: relative;
transform-style: preserve-3d;
transition: transform 0.5s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.card.flip {
transform: rotateY(180deg);
}
.card-front, .card-back {
width: 100%;
height: 100%;
position: absolute;
backface-visibility: hidden;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
font-size: 2em;
font-weight: bold;
}
.card-front {
background-color: #2196F3; /* Blue back of the card */
color: white;
transform: rotateY(0deg);
}
.card-back {
background-color: #4CAF50; /* Green front of the card */
color: white;
transform: rotateY(180deg);
}
.card.matched .card-front, .card.matched .card-back {
opacity: 0.5; /* Dim matched cards */
}
#resetButton {
padding: 10px 20px;
font-size: 1em;
background-color: #FF5722;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
}
#resetButton:hover {
background-color: #E64A19;
}
Key CSS Concepts:
- Flexbox: Used for centring the game container on the page.
- CSS Grid: Perfect for arranging our memory cards in a clean, responsive grid.
- 3D Transforms (
perspective
,transform-style
,rotateY
): These properties are essential for creating a satisfying card flip animation. backface-visibility: hidden;
: Ensures that the “back” of the card is hidden when rotated to show the “front.”- Transitions: Add smooth animations for the card flips and button hover effects.
Sure, here is an SEO-friendly blog post article about creating a JavaScript memory game:
SEO Title: Build Your Own JavaScript Memory Game: A Beginner-Friendly Tutorial
Meta Description: Learn how to create an engaging memory game using HTML, CSS, and JavaScript! This step-by-step tutorial is perfect for beginners looking to enhance their coding skills and build a fun project.
Build Your Own JavaScript Memory Game: A Beginner-Friendly Tutorial
Who doesn’t love a good memory game? They’re fun, engaging, and a fantastic way to give your brain a workout. But what if you could not only play one, but actually build one yourself? That’s exactly what we’re going to do in this tutorial!
Creating a memory game using HTML, CSS, and JavaScript is an excellent project for beginners and a great way to solidify your understanding of front-end web development. By the end of this article, you’ll have a fully functional memory game and a deeper appreciation for how these core web technologies work together.
Why Build a Memory Game?
Beyond the sheer fun of it, building a memory game offers numerous benefits for aspiring developers:
- HTML Structure: You’ll learn how to organize your game elements effectively using semantic HTML.
- CSS Styling: You’ll get hands-on experience with CSS to make your game visually appealing and responsive.
- JavaScript Logic: This is where the magic happens! You’ll dive into event handling, array manipulation, conditional logic, and timing functions – all crucial JavaScript concepts.
- Problem-Solving: You’ll encounter and solve common programming challenges, boosting your problem-solving skills.
- Portfolio Project: A working memory game is a fantastic addition to your portfolio, showcasing your front-end development abilities.
Getting Started: The Basic Setup
Before we dive into the code, let’s set up our project files. You’ll need three files:
index.html
(for the game structure)style.css
(for the game’s appearance)script.js
(for the game’s logic)
Create a new folder for your project and place these three empty files inside it.
Step 1: HTML Structure (index.html
)
Our HTML will provide the basic layout for our game. We’ll need a container for our game board and a place to display the score or status.
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Memory Game</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="game-container">
<h1>Memory Game</h1>
<div class="score-display">Score: <span id="score">0</span></div>
<div class="game-board" id="gameBoard"></div>
<button id="resetButton">Reset Game</button>
</div>
<script src="script.js"></script>
</body>
</html>
Explanation:
- We link our
style.css
file in the<head>
. - We have a main
game-container
to hold everything. - A
score-display
to show the player’s score. - The
game-board
div is where our memory cards will be dynamically generated by JavaScript. - A
resetButton
to start a new game. - Crucially, we link our
script.js
file just before the closing</body>
tag. This ensures the HTML is loaded before the JavaScript tries to manipulate it.
Step 2: CSS Styling (style.css
)
Now let’s make our game look good! We’ll add some basic styling to our containers and the memory cards themselves.
CSS
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f0f0;
margin: 0;
}
.game-container {
background-color: #fff;
padding: 30px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
}
h1 {
color: #333;
margin-bottom: 20px;
}
.score-display {
font-size: 1.2em;
margin-bottom: 20px;
}
.game-board {
display: grid;
grid-template-columns: repeat(4, 100px); /* Adjust as needed for more cards */
grid-gap: 10px;
perspective: 1000px; /* For flip animation */
margin: 0 auto 20px auto;
}
.card {
width: 100px;
height: 100px;
background-color: #ccc;
border-radius: 8px;
cursor: pointer;
position: relative;
transform-style: preserve-3d;
transition: transform 0.5s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.card.flip {
transform: rotateY(180deg);
}
.card-front, .card-back {
width: 100%;
height: 100%;
position: absolute;
backface-visibility: hidden;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
font-size: 2em;
font-weight: bold;
}
.card-front {
background-color: #2196F3; /* Blue back of the card */
color: white;
transform: rotateY(0deg);
}
.card-back {
background-color: #4CAF50; /* Green front of the card */
color: white;
transform: rotateY(180deg);
}
.card.matched .card-front, .card.matched .card-back {
opacity: 0.5; /* Dim matched cards */
}
#resetButton {
padding: 10px 20px;
font-size: 1em;
background-color: #FF5722;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
}
#resetButton:hover {
background-color: #E64A19;
}
Key CSS Concepts:
- Flexbox: Used for centering the game container on the page.
- CSS Grid: Perfect for arranging our memory cards in a clean, responsive grid.
- 3D Transforms (
perspective
,transform-style
,rotateY
): These properties are essential for creating a satisfying card flip animation. backface-visibility: hidden;
: Ensures that the “back” of the card is hidden when rotated to show the “front.”- Transitions: Add smooth animations for the card flips and button hover effects.
Step 3: JavaScript Logic (script.js
)
This is where the real game logic comes alive! We’ll handle card generation, shuffling, clicking, matching, and game state.
const gameBoard = document.getElementById('gameBoard');
const scoreDisplay = document.getElementById('score');
const resetButton = document.getElementById('resetButton');
let cardsArray = [
{ name: 'apple', img: '🍎' },
{ name: 'banana', img: '🍌' },
{ name: 'cherry', img: '🍒' },
{ name: 'grape', img: '🍇' },
{ name: 'lemon', img: '🍋' },
{ name: 'orange', img: '🍊' },
{ name: 'peach', img: '🍑' },
{ name: 'strawberry', img: '🍓' },
];
// Duplicate cards for pairs
let gameCards = [...cardsArray, ...cardsArray];
let flippedCards = [];
let matchedPairs = 0;
let score = 0;
let lockBoard = false; // To prevent rapid clicking during comparison
function shuffleCards() {
gameCards.sort(() => 0.5 - Math.random());
}
function createBoard() {
shuffleCards();
gameBoard.innerHTML = ''; // Clear existing board
score = 0;
scoreDisplay.textContent = score;
matchedPairs = 0;
flippedCards = [];
lockBoard = false;
gameCards.forEach((card, index) => {
const cardElement = document.createElement('div');
cardElement.classList.add('card');
cardElement.dataset.name = card.name;
cardElement.dataset.id = index; // Unique ID for each instance of a card
const cardFront = document.createElement('div');
cardFront.classList.add('card-front');
cardFront.textContent = '?'; // Or a symbol for the back of the card
const cardBack = document.createElement('div');
cardBack.classList.add('card-back');
cardBack.textContent = card.img;
cardElement.appendChild(cardFront);
cardElement.appendChild(cardBack);
cardElement.addEventListener('click', flipCard);
gameBoard.appendChild(cardElement);
});
}
function flipCard() {
if (lockBoard) return;
if (this === flippedCards[0]) return; // Prevent clicking the same card twice
this.classList.add('flip');
flippedCards.push(this);
if (flippedCards.length === 2) {
lockBoard = true;
setTimeout(checkForMatch, 1000); // Check for match after a short delay
}
}
function checkForMatch() {
const [firstCard, secondCard] = flippedCards;
if (firstCard.dataset.name === secondCard.dataset.name) {
// It's a match!
firstCard.removeEventListener('click', flipCard);
secondCard.removeEventListener('click', flipCard);
firstCard.classList.add('matched');
secondCard.classList.add('matched');
matchedPairs++;
score += 10; // Increase score for a match
scoreDisplay.textContent = score;
resetFlippedCards();
if (matchedPairs === cardsArray.length) {
setTimeout(() => {
alert('Congratulations! You matched all pairs!');
}, 500);
}
} else {
// Not a match
setTimeout(() => {
firstCard.classList.remove('flip');
secondCard.classList.remove('flip');
resetFlippedCards();
}, 1000); // Flip back after a delay
}
}
function resetFlippedCards() {
flippedCards = [];
lockBoard = false;
}
// Event listener for the reset button
resetButton.addEventListener('click', createBoard);
// Initial board creation when the page loads
createBoard();
Core JavaScript Concepts Explained
cardsArray
andgameCards
: We start with an array of unique card data, then duplicate it to create pairs for the game.shuffleCards()
: This function usesMath.random()
to randomly sort thegameCards
array, ensuring a new game layout every time.createBoard()
: This function dynamically generates the card elements in our HTML. It loops throughgameCards
, createsdiv
elements for each card (front and back), sets theirdataset.name
(important for matching), and attaches aclick
event listener.flipCard()
: This is the event handler for when a card is clicked. It adds theflip
class to visually rotate the card. It also stores the clicked cards inflippedCards
and, if two cards are flipped, callscheckForMatch
.checkForMatch()
: This is the heart of the game logic. It compares thedataset.name
of the two flipped cards.- Match: If they match, the cards remain flipped, their event listeners are removed (so they can’t be clicked again), and
matchedPairs
andscore
are updated. We check if all pairs are matched to declare victory. - No Match: If they don’t match, after a brief delay (to allow the player to see both cards), the
flip
class is removed, and they return to their face-down state.
- Match: If they match, the cards remain flipped, their event listeners are removed (so they can’t be clicked again), and
lockBoard
: This crucial boolean variable prevents the player from clicking more cards while two cards are being compared, avoiding glitches.resetButton
: An event listener is added to the reset button to callcreateBoard()
and start a new game.- Initial Call:
createBoard()
It is called once when the script loads to set up the very first game board.
Git Repository: Click here
Enhancements and Next Steps
- More Card Themes: Instead of just emojis, use images for your card faces.
- Difficulty Levels: Support different sizes of grids for players (e.g., a 4×4 card grid, a 6×6 grid).
- Timer: Add a timer that tracks how long it took before they completed their turn.
- Move Counter: Track how many moves they made.
- Sound Effects: Add sounds when cards are flipped, matched, or the game is won.
- High Scores: Implement high scores, save high scores with local Storage, and display them.
- Responsiveness: Improve how your CSS and layouts respond so that it looks good on smaller as well as larger screen sizes.