๐ฃ Free game 15 of 20 ยท copy & paste
Minesweeper Game in HTML & JavaScript: Free Code + Demo
Classic Minesweeper with a safe first click: open the squares, read the numbers and flag every mine. Works with a mouse (right-click to flag) and on phones (long-press or Flag mode). Copy the code below, save it as minesweeper.html and open it in any browser.
- Type
- Puzzle
- Level
- Intermediate
- Code
- 120 lines, 1 file
- Libraries
- None
- Works on
- Phones & computers
- You learn
- Flood fill and counting neighbours
โฒ Play it right here. Click to open. Right-click, long-press or Flag mode to place a flag.
What's in this free Minesweeper game
- The first click is always safe
- Opens empty areas automatically
- Right-click, long-press and Flag mode
- Mine counter and timer
- Change the size and number of mines
The code (120 lines, one file)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Minesweeper</title>
<!--
Free HTML Minesweeper game โ one file, no libraries. Save it as minesweeper.html and open it in a browser.
From htmlgamescode.com (free to use in your own projects).
Click to open a square. Right-click, long-press or use Flag mode to mark a mine. Your first click is always safe.
-->
<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;
user-select: none; -webkit-user-select: none; touch-action: manipulation; padding: 12px;
}
.bar { width: min(94vw, 400px); display: flex; justify-content: space-between; align-items: center; gap: 6px; font-weight: 800; }
.bar span { background: #2a2170; padding: 6px 10px; border-radius: 10px; }
.bar button { border: 0; border-radius: 10px; padding: 7px 10px; font: 700 15px system-ui, sans-serif; color: #fff; background: #382c8f; cursor: pointer; }
.bar button.on { background: #ff5fb1; }
.grid { display: grid; gap: 3px; width: min(94vw, 400px, calc(100vh - 120px)); padding: 6px; background: #382c8f; border-radius: 12px; }
.grid button {
aspect-ratio: 1; border: 0; border-radius: 5px; background: #6f5cf0; color: #fff; font: 900 min(4.6vw, 20px) system-ui, sans-serif;
box-shadow: inset 0 -3px 0 rgba(0,0,0,.25); cursor: pointer; padding: 0;
}
.grid button.open { background: #e9e5ff; box-shadow: none; cursor: default; }
.grid button.boom { background: #ff4d6d; }
.status { font-size: 20px; font-weight: 800; min-height: 28px; }
</style>
</head>
<body>
<div class="bar"><span>๐ฃ <b id="left">0</b></span><button id="flagMode">๐ฉ Flag: off</button><button id="reset">๐ New</button><span>โฑ <b id="time">0</b></span></div>
<div class="grid" id="grid"></div>
<div class="status" id="status">Find all the safe squares</div>
<script>
// ---- settings: change these to make it your own ----
const ROWS = 9; // board height
const COLS = 9; // board width
const MINES = 10; // number of mines (try 16x16 with 40 for "medium")
const NUMBER_COLORS = ["", "#2f6bff", "#18a34a", "#e8343f", "#6b2fd6", "#b3261e", "#0e9aa7", "#222", "#666"];
const $ = id => document.getElementById(id);
const grid = $("grid");
grid.style.gridTemplateColumns = `repeat(${COLS}, 1fr)`;
let cells, started, over, flagMode = false, opened, timer, t0;
function newGame() {
grid.innerHTML = ""; started = false; over = false; opened = 0;
clearInterval(timer); $("time").textContent = 0; $("reset").textContent = "๐ New"; $("status").textContent = "Find all the safe squares";
cells = [];
for (let r = 0; r < ROWS; r++) for (let c = 0; c < COLS; c++) {
const b = document.createElement("button");
const cell = { r, c, mine: false, open: false, flag: false, n: 0, el: b };
b.setAttribute("aria-label", `Row ${r + 1} column ${c + 1}`);
let press; // long-press = flag (phones); the click that follows it must not open the square
b.addEventListener("pointerdown", e => { cell.skipClick = false; if (e.pointerType !== "mouse") press = setTimeout(() => { cell.skipClick = true; toggleFlag(cell); }, 380); });
["pointerup", "pointerleave", "pointercancel"].forEach(t => b.addEventListener(t, () => clearTimeout(press)));
b.addEventListener("click", () => { if (cell.skipClick) { cell.skipClick = false; return; } flagMode ? toggleFlag(cell) : reveal(cell); });
b.addEventListener("contextmenu", e => { e.preventDefault(); toggleFlag(cell); });
grid.appendChild(b); cells.push(cell);
}
updateLeft();
}
const at = (r, c) => r >= 0 && r < ROWS && c >= 0 && c < COLS ? cells[r * COLS + c] : null;
const around = cell => { const out = []; for (let dr = -1; dr <= 1; dr++) for (let dc = -1; dc <= 1; dc++) { const n = (dr || dc) && at(cell.r + dr, cell.c + dc); if (n) out.push(n); } return out; };
// mines are placed after the first click, never on or next to it
function placeMines(safe) {
const banned = new Set([safe, ...around(safe)]);
const spots = cells.filter(c => !banned.has(c));
for (let i = 0; i < MINES && spots.length; i++) spots.splice(Math.floor(Math.random() * spots.length), 1)[0].mine = true;
cells.forEach(c => { c.n = around(c).filter(n => n.mine).length; });
}
function reveal(cell) {
if (over || cell.open || cell.flag) return;
if (!started) { started = true; placeMines(cell); t0 = Date.now(); timer = setInterval(() => { $("time").textContent = Math.floor((Date.now() - t0) / 1000); }, 1000); }
if (cell.mine) return lose(cell);
const stack = [cell]; // flood fill: open every empty area at once
while (stack.length) {
const c = stack.pop();
if (c.open || c.flag) continue;
c.open = true; opened++;
c.el.className = "open"; c.el.textContent = c.n || ""; c.el.style.color = NUMBER_COLORS[c.n];
if (c.n === 0) around(c).forEach(n => !n.open && stack.push(n));
}
if (opened === ROWS * COLS - MINES) win();
}
function toggleFlag(cell) {
if (over || cell.open) return;
cell.flag = !cell.flag; cell.el.textContent = cell.flag ? "๐ฉ" : "";
updateLeft();
}
function updateLeft() { $("left").textContent = MINES - cells.filter(c => c.flag).length; }
function lose(cell) {
over = true; clearInterval(timer);
cells.forEach(c => { if (c.mine) { c.el.textContent = "๐ฃ"; c.el.className = "open"; } });
cell.el.className = "open boom";
$("reset").textContent = "๐ต New"; $("status").textContent = "๐ฅ Boom! Try again.";
}
function win() {
over = true; clearInterval(timer);
cells.forEach(c => { if (c.mine) c.el.textContent = "๐ฉ"; });
$("reset").textContent = "๐ New"; $("status").textContent = `๐ You cleared it in ${$("time").textContent} seconds!`;
}
$("reset").addEventListener("click", newGame);
$("flagMode").addEventListener("click", () => { flagMode = !flagMode; $("flagMode").textContent = `๐ฉ Flag: ${flagMode ? "on" : "off"}`; $("flagMode").classList.toggle("on", flagMode); });
newGame();
</script>
</body>
</html>
How to use it
- Click Copy code (or Download).
- Paste it into any text editor and save it as
minesweeper.html. - Open the file in Chrome, Safari, Firefox or Edge. That's it, it runs offline.
How the code works
Placing mines
Mines are placed after your first click, never on it or next to it. Then every square counts its neighbouring mines.
Flood fill
Opening a square with 0 mines around it opens its neighbours too. A stack (not recursion) handles big empty areas.
Winning
You win when every square that isn't a mine is open.
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 |
|---|---|---|
ROWS | 9 | board height |
COLS | 9 | board width |
MINES | 10 | number of mines (try 16x16 with 40 for "medium") |
Put it on your website
Upload minesweeper.html next to your page and add this where the game should appear:
<iframe src="minesweeper.html" width="460" height="620" style="border:0;max-width:100%" title="Minesweeper game"></iframe>
FAQ
Is this Minesweeper 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 Minesweeper game?
Copy the code, paste it into a text editor, save the file as minesweeper.html and double-click it. It opens in your browser and works offline; there is nothing to install.
What are the controls?
Click to open. Right-click, long-press or Flag mode to place a flag.
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 minesweeper.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.