๐ Free game 6 of 20 ยท copy & paste
Memory Card Game in HTML, CSS & JavaScript: Free Code
Flip two cards at a time and find all the pairs in as few moves as you can. The cards flip with a CSS 3D animation and use emoji, so there are no images. Copy the code below, save it as memory-game.html and open it in any browser.
- Type
- Puzzle
- Level
- Easy
- Code
- 111 lines, 1 file
- Libraries
- None
- Works on
- Phones & computers
- You learn
- FisherโYates shuffle and 3D card flips in CSS
โฒ Play it right here. Click or tap a card to flip it.
What's in this free Memory Card Game game
- CSS 3D flip animation
- Moves counter, timer and best score
- Emoji cards: swap them for any set
- Change the number of pairs and columns
- No images, no libraries
The code (111 lines, one file)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Memory Card Game</title>
<!--
Free HTML Memory (card matching) game โ one file, no libraries. Save it as memory-game.html and open it in a browser.
From htmlgamescode.com (free to use in your own projects).
Flip two cards at a time and find all the pairs in as few moves as you can.
-->
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
background: #1b1446; color: #fff; font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
padding: 12px; user-select: none; -webkit-user-select: none;
}
.bar { width: min(92vw, 400px); display: flex; justify-content: space-between; gap: 6px; font-weight: 700; }
.bar span { background: #2a2170; padding: 6px 10px; border-radius: 10px; }
.grid { display: grid; gap: 10px; width: min(92vw, 400px, calc(100vh - 130px)); }
.card { position: relative; aspect-ratio: 1; border: 0; background: none; cursor: pointer; transform-style: preserve-3d; transition: transform .35s; }
.card.open { transform: rotateY(180deg); }
.card span {
position: absolute; inset: 0; display: grid; place-items: center; border-radius: 16px; backface-visibility: hidden;
-webkit-backface-visibility: hidden; font-size: min(10vw, 42px, calc((100vh - 130px) / 9)); box-shadow: 0 5px 0 rgba(0,0,0,.3);
}
.card .back { background: linear-gradient(135deg, #ff5fb1, #8b5cff); color: #fff; font-weight: 900; }
.card .front { background: #fff; transform: rotateY(180deg); }
.card.done .front { background: #b9ffc8; }
.msg { position: fixed; inset: 0; display: grid; place-items: center; background: rgba(10, 6, 36, .7); }
.msg[hidden] { display: none; }
.box { text-align: center; background: #2a2170; padding: 26px 30px; border-radius: 20px; }
.box h1 { font-size: 28px; margin-bottom: 6px; }
.box p { opacity: .85; margin-bottom: 16px; }
.btn { border: 0; border-radius: 14px; padding: 12px 28px; font-size: 18px; font-weight: 700; color: #fff; background: #36d35a; box-shadow: 0 4px 0 #1d9a3a; cursor: pointer; }
</style>
</head>
<body>
<div class="bar"><span>Moves: <b id="moves">0</b></span><span>Time: <b id="time">0</b>s</span><span>Best: <b id="best">โ</b></span></div>
<div class="grid" id="grid"></div>
<button class="btn" id="restart">Shuffle & restart</button>
<div class="msg" id="msg" hidden><div class="box"><h1>๐ You did it!</h1><p id="result"></p><button class="btn" id="again">Play again</button></div></div>
<script>
// ---- settings: change these to make it your own ----
const EMOJIS = ["๐", "๐", "๐", "๐", "๐", "๐ฅ", "๐", "๐"]; // one pair of cards per emoji (8 = 16 cards)
const COLUMNS = 4; // cards per row
const FLIP_BACK_MS = 800; // how long two wrong cards stay face up
const $ = id => document.getElementById(id);
const load = k => { try { return Number(localStorage.getItem(k)) || 0; } catch { return 0; } };
const save = (k, v) => { try { localStorage.setItem(k, v); } catch {} };
let first, lock, moves, matched, started, timer;
let best = load("memory-best");
function shuffle(a) { // FisherโYates shuffle
for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; }
return a;
}
function deal() {
const grid = $("grid");
grid.innerHTML = "";
grid.style.gridTemplateColumns = `repeat(${COLUMNS}, 1fr)`;
shuffle([...EMOJIS, ...EMOJIS]).forEach(e => {
const c = document.createElement("button");
c.className = "card"; c.dataset.e = e; c.setAttribute("aria-label", "Hidden card");
c.innerHTML = `<span class="back">?</span><span class="front">${e}</span>`;
c.addEventListener("click", () => flip(c));
grid.appendChild(c);
});
first = null; lock = false; moves = 0; matched = 0; started = 0;
clearInterval(timer); $("msg").hidden = true; update();
}
function flip(c) {
if (lock || c === first || c.classList.contains("open")) return;
if (!started) { started = Date.now(); timer = setInterval(update, 1000); }
c.classList.add("open");
if (!first) { first = c; return; }
moves++; update();
if (first.dataset.e === c.dataset.e) { // a pair!
first.classList.add("done"); c.classList.add("done");
first = null; matched++;
if (matched === EMOJIS.length) win();
} else { // not a pair: flip both back
lock = true;
const a = first; first = null;
setTimeout(() => { a.classList.remove("open"); c.classList.remove("open"); lock = false; }, FLIP_BACK_MS);
}
}
function seconds() { return started ? Math.floor((Date.now() - started) / 1000) : 0; }
function update() { $("moves").textContent = moves; $("time").textContent = seconds(); $("best").textContent = best || "โ"; }
function win() {
clearInterval(timer);
const newBest = !best || moves < best;
if (newBest) { best = moves; save("memory-best", best); }
$("result").textContent = `${moves} moves in ${seconds()} seconds.${newBest ? " New best! ๐" : ""}`;
update();
setTimeout(() => { $("msg").hidden = false; }, 500);
}
$("restart").addEventListener("click", deal);
$("again").addEventListener("click", deal);
deal();
</script>
</body>
</html>
How to use it
- Click Copy code (or Download).
- Paste it into any text editor and save it as
memory-game.html. - Open the file in Chrome, Safari, Firefox or Edge. That's it, it runs offline.
How the code works
Shuffling
The emoji list is doubled and shuffled with the FisherโYates shuffle, which gives every order the same chance.
Flipping
Each card has a front and a back face with backface-visibility hidden. Adding the open class rotates the card 180ยฐ.
Checking a pair
The first card is remembered. When the second one opens, matching cards stay open; otherwise both flip back after FLIP_BACK_MS.
Make it your own
All the settings are at the top of the script. Change a value, save and reload:
| Setting | Default | What it does |
|---|---|---|
EMOJIS | a list | one pair of cards per emoji (8 = 16 cards) |
COLUMNS | 4 | cards per row |
FLIP_BACK_MS | 800 | how long two wrong cards stay face up |
Put it on your website
Upload memory-game.html next to your page and add this where the game should appear:
<iframe src="memory-game.html" width="460" height="620" style="border:0;max-width:100%" title="Memory Card Game game"></iframe>
FAQ
Is this Memory Card Game code really free?
Yes. Copy it and use it in your own website, school project or app, change it as much as you like and publish it, even commercially. A link back to HTML Games Code is welcome but not required.
How do I run the Memory Card Game game?
Copy the code, paste it into a text editor, save the file as memory-game.html and double-click it. It opens in your browser and works offline; there is nothing to install.
What are the controls?
Click or tap a card to flip it.
Does it work on phones?
Yes. It has touch controls and fits a phone screen, and it works with a keyboard and mouse on a computer.
Can I put it on my website?
Yes. Upload memory-game.html and link to it, or show it inside an iframe on any page (see "Put it on your website" above).
Can I turn it into an Android app?
Yes. Any HTML game can be packaged as an Android APK. Our HTML Games APK Builder does it for our premium games, or you can use a tool like htmltoapk.com for this file.