In short
In this beginner JavaScript project, you will build a number guessing game step by step, using functions, conditions and variables in a real web page. What you will build The computer picks a secret number between 1 and 100. The player types a guess and clicks a button.
In this beginner JavaScript project, you will build a number guessing game step by step, using functions, conditions and variables in a real web page.
What you will build
The computer picks a secret number between 1 and 100. The player types a guess and clicks a button. The game says "too high" or "too low", counts the attempts, and shows a "Play again" button when the player wins.
You will use almost everything from the Beginner modules: variables, if/else, comparison with ===, type conversion with Number(), template literals, the ternary operator and functions. You will also meet three new tools that connect JavaScript to the page:
document.querySelector()finds an element on the page, like a search box for your HTML.addEventListener()tells an element "when this happens (for example a click), run this function".textContentreads or changes the text inside an element.
Note: These three are part of the DOM (Document Object Model), the way JavaScript sees and changes a web page. You will learn the DOM properly in the DOM module. For now, just follow along and focus on the logic.
Create a folder called guessing-game with two empty files: index.html and game.js.
Step 1: Build the HTML page
Put this into index.html. It has a heading, an input box for the guess, a "Check" button, a paragraph for messages, a paragraph for the attempts count, and a hidden "Play again" button.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Number Guessing Game</title>
</head>
<body>
<main>
<h1>Guess the Number</h1>
<p>I am thinking of a number between 1 and 100. Can you find it?</p>
<label for="guess-input">Your guess:</label>
<input type="number" id="guess-input" min="1" max="100">
<button id="guess-button">Check</button>
<p id="message">Make your first guess!</p>
<p id="attempts">Attempts: 0</p>
<button id="restart-button" hidden>Play again</button>
</main>
<script src="game.js"></script>
</body>
</html>
Each element we need to control has an id, a unique name we will use to find it from JavaScript. The hidden attribute hides the restart button until the game is won. The <script> tag is at the end of the body, so the page is fully loaded before the script runs. Open the file in your browser: you will see the page, but clicking "Check" does nothing yet.
Step 2: Pick a secret number and find the elements
Now open game.js. First, we find the elements and store them in constants. Then we create the secret number and the attempts counter.
// Find the elements on the page
const guessInput = document.querySelector("#guess-input");
const guessButton = document.querySelector("#guess-button");
const message = document.querySelector("#message");
const attemptsText = document.querySelector("#attempts");
const restartButton = document.querySelector("#restart-button");
// Create a random whole number from 1 to 100
function getRandomNumber() {
return Math.floor(Math.random() * 100) + 1;
}
// Game state
let secretNumber = getRandomNumber();
let attempts = 0;
console.log(`Secret number (for testing): ${secretNumber}`);
// Output (random, for example): Secret number (for testing): 42
document.querySelector("#guess-input") finds the element whose id is guess-input. The # means "id", the same as in CSS. Math.random() gives a decimal from 0 up to (but not including) 1. Multiplying by 100 gives 0 to 99.99..., Math.floor() rounds down to 0–99, and + 1 shifts it to 1–100.
We use let for secretNumber and attempts because they will change. The console.log line lets you peek at the answer while testing. Open the console (F12) and reload to see it.
Tip: Remove the "for testing" line when you share the game with friends, or they can cheat by opening the console.
Step 3: Check the guess and give higher/lower hints
Add this function below the code from Step 2. It runs every time the player clicks "Check".
function checkGuess() {
const guess = Number(guessInput.value);
// 1. Reject empty or invalid input (return early)
if (guessInput.value === "" || !Number.isInteger(guess) || guess < 1 || guess > 100) {
message.textContent = "Please enter a whole number from 1 to 100.";
return;
}
// 2. Count this attempt
attempts++;
attemptsText.textContent = `Attempts: ${attempts}`;
// 3. Compare with the secret number
if (guess === secretNumber) {
const word = attempts === 1 ? "attempt" : "attempts";
message.textContent = `Correct! The number was ${secretNumber}. You got it in ${attempts} ${word}.`;
endGame();
} else if (guess < secretNumber) {
message.textContent = `${guess} is too low. Try a higher number.`;
} else {
message.textContent = `${guess} is too high. Try a lower number.`;
}
// 4. Clear the box for the next guess
guessInput.value = "";
guessInput.focus();
}
guessButton.addEventListener("click", checkGuess);
Let's walk through it:
guessInput.valueis what the player typed. It is always a string, even in a number input, so we convert it withNumber().- The first
ifchecks for bad input.Number.isInteger(guess)istrueonly for whole numbers, so2.5is rejected. If anything is wrong, we show a message andreturnearly, so the attempt is not counted. attempts++adds one, andtextContentupdates the paragraph on the page.- The
if/else if/elsechain gives the hint. The ternary picks "attempt" or "attempts" so the sentence is correct. guessInput.focus()puts the cursor back in the box so the player can type again right away.- The last line connects everything: when the button is clicked, run
checkGuess.
Common mistake: Writing
addEventListener("click", checkGuess())with brackets. That calls the function once immediately, when the page loads, instead of on every click. Pass the function name without brackets.
Reload the page and play. The hints should work, but after you win, you can keep guessing. Let's fix that.
Step 4: End the game and add a restart button
Add these two functions and one more event listener at the bottom of game.js:
function endGame() {
guessInput.disabled = true;
guessButton.disabled = true;
restartButton.hidden = false;
}
function restartGame() {
secretNumber = getRandomNumber();
attempts = 0;
attemptsText.textContent = "Attempts: 0";
message.textContent = "New game! Make your first guess.";
guessInput.disabled = false;
guessButton.disabled = false;
restartButton.hidden = true;
guessInput.value = "";
guessInput.focus();
console.log(`Secret number (for testing): ${secretNumber}`);
}
restartButton.addEventListener("click", restartGame);
endGame() turns off the input and the "Check" button by setting disabled to true, and shows the restart button by setting hidden to false. restartGame() does the opposite and resets the game state: a new secret number and zero attempts. Because secretNumber and attempts were created with let at the top level of the file, both functions can read and change them. In a small project like this, that shared state is fine; in bigger programs you would keep it more contained, as you learned in the Scope lesson.
Notice that checkGuess calls endGame(), even though endGame is written further down the file. That works because function declarations are hoisted, as you saw in the Functions lesson.
Complete final code
Here is the full project. index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Number Guessing Game</title>
</head>
<body>
<main>
<h1>Guess the Number</h1>
<p>I am thinking of a number between 1 and 100. Can you find it?</p>
<label for="guess-input">Your guess:</label>
<input type="number" id="guess-input" min="1" max="100">
<button id="guess-button">Check</button>
<p id="message">Make your first guess!</p>
<p id="attempts">Attempts: 0</p>
<button id="restart-button" hidden>Play again</button>
</main>
<script src="game.js"></script>
</body>
</html>
And game.js:
// Find the elements on the page
const guessInput = document.querySelector("#guess-input");
const guessButton = document.querySelector("#guess-button");
const message = document.querySelector("#message");
const attemptsText = document.querySelector("#attempts");
const restartButton = document.querySelector("#restart-button");
// Create a random whole number from 1 to 100
function getRandomNumber() {
return Math.floor(Math.random() * 100) + 1;
}
// Game state
let secretNumber = getRandomNumber();
let attempts = 0;
function checkGuess() {
const guess = Number(guessInput.value);
if (guessInput.value === "" || !Number.isInteger(guess) || guess < 1 || guess > 100) {
message.textContent = "Please enter a whole number from 1 to 100.";
return;
}
attempts++;
attemptsText.textContent = `Attempts: ${attempts}`;
if (guess === secretNumber) {
const word = attempts === 1 ? "attempt" : "attempts";
message.textContent = `Correct! The number was ${secretNumber}. You got it in ${attempts} ${word}.`;
endGame();
} else if (guess < secretNumber) {
message.textContent = `${guess} is too low. Try a higher number.`;
} else {
message.textContent = `${guess} is too high. Try a lower number.`;
}
guessInput.value = "";
guessInput.focus();
}
function endGame() {
guessInput.disabled = true;
guessButton.disabled = true;
restartButton.hidden = false;
}
function restartGame() {
secretNumber = getRandomNumber();
attempts = 0;
attemptsText.textContent = "Attempts: 0";
message.textContent = "New game! Make your first guess.";
guessInput.disabled = false;
guessButton.disabled = false;
restartButton.hidden = true;
guessInput.value = "";
guessInput.focus();
}
guessButton.addEventListener("click", checkGuess);
restartButton.addEventListener("click", restartGame);
Open index.html in your browser. A typical game looks like this:
You type 50 -> "50 is too high. Try a lower number." Attempts: 1
You type 25 -> "25 is too low. Try a higher number." Attempts: 2
You type 37 -> "Correct! The number was 37. You got it in 3 attempts."
The input and Check button turn off, and "Play again" appears.
Ideas to extend the game
Once it works, make it your own. Each idea uses only what you already know:
- Limit the attempts: give the player 10 tries. If
attemptsreaches 10 without a win, show "Game over! The number was ..." and callendGame(). - Best score: keep a
let bestScorevariable and update it when the player wins in fewer attempts than before. - Warmer/colder hints: if the guess is within 5 of the secret number, say "Very close!".
- Difficulty levels: change the range to 1–10 (easy) or 1–1000 (hard) by giving
getRandomNumberamaxparameter with a default value of 100. - Press Enter to guess: listen for the
"keydown"event on the input and callcheckGuess()whenevent.key === "Enter". You will learn events in depth in the DOM module.
Practice exercise
Add the "Limit the attempts" feature. Create const maxAttempts = 10; near the top. Show the remaining tries in the attempts paragraph, for example Attempts: 3 (7 left), and end the game when no tries are left.
Hint: After attempts++, calculate maxAttempts - attempts. In the final else branches, check if (attempts === maxAttempts) after setting the hint, then change the message and call endGame(). Remember to keep the win check first, so a correct guess on the last try still wins.
Key takeaways
- A real project combines small pieces: variables for state, functions for actions, and conditions for decisions.
document.querySelector()finds elements,addEventListener()runs a function on an event, andtextContentchanges text on the page.- Input values are always strings, so convert them with
Number()and validate them before use. - Returning early keeps invalid input from being counted.
- Splitting the game into
checkGuess,endGameandrestartGamemakes the code easy to read and extend.