🧁

Cupcake Clicker

A JavaScript game tutorial by She Codes Australia

Cupcakes are popping up all over the screen — click them before they disappear! In this tutorial you're going to write all the JavaScript that makes this game work, step by step.

By the end you'll have a fully working game and you'll have used variables, functions, events, timers, and the DOM — the core building blocks of JavaScript.

Open your CodePen starter ↗
💡 Before you start

Your CodePen starter already has the HTML and CSS set up — the screens and buttons are there and waiting. Your only job today is to write the JavaScript in the JS panel.

Step 1

Your Variables

Every game needs to keep track of information — like the player's score and how much time is left. In JavaScript, we use variables to store that information.

📦 What is a variable?

A variable is like a labelled box. You give it a name and store a value inside. Later you can read that value, or replace it with something new.

We also need to grab the elements from our HTML page so JavaScript can talk to them. We do this using document.getElementById() — it finds an element by its id and hands it back to us.

Add the following to the top of your JS panel:

// Game state — these values change as the game is played
let score    = 0;
let timeLeft = 30;

// Timers — we'll use these to start and stop things later
let gameTimerInterval  = null;
let spawnTimerInterval = null;

// Grab the elements we need from the HTML
const screenStart = document.getElementById("screen-start");
const screenGame  = document.getElementById("screen-game");
const screenEnd   = document.getElementById("screen-end");

const scoreDisplay = document.getElementById("score-display");
const timerDisplay = document.getElementById("timer-display");
const finalScore   = document.getElementById("final-score");
const endMessage   = document.getElementById("end-message");
const gameArea     = document.getElementById("game-area");

const btnStart   = document.getElementById("btn-start");
const btnRestart = document.getElementById("btn-restart");

To make sure everything is connected, add this line at the very bottom and check your console:

console.log("Score is:", score);
✅ Try it! Open the console in CodePen (the small icon in the bottom-left). You should see Score is: 0 printed out. Once you've confirmed it works, delete that console.log line.
👀 Check your code
let score    = 0;
let timeLeft = 30;

let gameTimerInterval  = null;
let spawnTimerInterval = null;

const screenStart = document.getElementById("screen-start");
const screenGame  = document.getElementById("screen-game");
const screenEnd   = document.getElementById("screen-end");

const scoreDisplay = document.getElementById("score-display");
const timerDisplay = document.getElementById("timer-display");
const finalScore   = document.getElementById("final-score");
const endMessage   = document.getElementById("end-message");
const gameArea     = document.getElementById("game-area");

const btnStart   = document.getElementById("btn-start");
const btnRestart = document.getElementById("btn-restart");
Step 2

Let's Get Started

Right now the Start button just sits there looking pretty. Let's bring it to life! We're going to write two functions — one to switch between screens, and one to kick off the game when the button is clicked.

🔧 What is a function?

A function is a reusable block of code that does one job. You give it a name, and whenever you want that job done, you call it by writing its name followed by ().

Add these two functions below your variables:

// Hide all screens, then show the one we want
function showScreen(screenToShow) {
  screenStart.classList.add("hidden");
  screenGame.classList.add("hidden");
  screenEnd.classList.add("hidden");

  screenToShow.classList.remove("hidden");
}

// Start the game — we'll build this up over the next few steps
function startGame() {
  score    = 0;
  timeLeft = 30;
  gameArea.innerHTML = "";

  showScreen(screenGame);
}

Now we need to listen for the Start button being clicked. Add this at the very bottom of your code:

btnStart.addEventListener("click", startGame);
👂 What is an event listener?

An event listener watches an element and waits for something to happen — like a click. When it does, it runs a function. Think of it like a doorbell: press the button (the event), and the bell rings (the function runs).

✅ Try it! Click the Let's Play! button on your start screen — you should be taken through to the game area. You can't get back yet, but we'll fix that later!
👀 Check your code
// ... variables from Step 1 ...

function showScreen(screenToShow) {
  screenStart.classList.add("hidden");
  screenGame.classList.add("hidden");
  screenEnd.classList.add("hidden");

  screenToShow.classList.remove("hidden");
}

function startGame() {
  score    = 0;
  timeLeft = 30;
  gameArea.innerHTML = "";

  showScreen(screenGame);
}

btnStart.addEventListener("click", startGame);
Step 3

Show the Score on Screen

We're in the game area now — but the score and timer in the HUD are still empty. Let's write a function that pushes our variable values onto the screen.

Add this function below showScreen:

function updateHUD() {
  scoreDisplay.textContent = score;
  timerDisplay.textContent = timeLeft;
}

Then call it inside startGame, right after showScreen(screenGame):

function startGame() {
  score    = 0;
  timeLeft = 30;
  gameArea.innerHTML = "";

  showScreen(screenGame);
  updateHUD(); // ← add this line
}
✅ Try it! Click Let's Play! again. You should now see 0 for the score and 30 for the time in the HUD — powered by your JavaScript variables!
👀 Check your code
// ... variables from Step 1 ...

function showScreen(screenToShow) {
  screenStart.classList.add("hidden");
  screenGame.classList.add("hidden");
  screenEnd.classList.add("hidden");

  screenToShow.classList.remove("hidden");
}

function updateHUD() {
  scoreDisplay.textContent = score;
  timerDisplay.textContent = timeLeft;
}

function startGame() {
  score    = 0;
  timeLeft = 30;
  gameArea.innerHTML = "";

  showScreen(screenGame);
  updateHUD();
}

btnStart.addEventListener("click", startGame);
Step 4

Summon a Cupcake!

Now for the fun part — let's make a cupcake appear on screen! We're going to write a function that creates a new cupcake element and drops it somewhere random in the game area.

Add this function below updateHUD:

function spawnCupcake() {
  const cupcakes = ["🧁", "🍰", "🎂", "🍩", "🍪"];

  // Pick a random cupcake emoji from the list
  const randomIndex = Math.floor(Math.random() * cupcakes.length);
  const emoji = cupcakes[randomIndex];

  // Create a new <div> and give it the "cupcake" class
  const cupcake = document.createElement("div");
  cupcake.classList.add("cupcake");
  cupcake.textContent = emoji;

  // Place it at a random position inside the game area
  const gameWidth  = gameArea.offsetWidth;
  const gameHeight = gameArea.offsetHeight;

  cupcake.style.left = Math.floor(Math.random() * (gameWidth  - 80)) + "px";
  cupcake.style.top  = Math.floor(Math.random() * (gameHeight - 80)) + "px";

  // Add it to the page so it actually appears
  gameArea.appendChild(cupcake);
}
🎲 What is Math.random()?

Math.random() gives you a random decimal number between 0 and 1 (like 0.472). Multiply it by a larger number and use Math.floor() to round it down, and you get a random whole number in whatever range you need.

Now call it at the end of startGame so a cupcake appears as soon as the game loads:

function startGame() {
  score    = 0;
  timeLeft = 30;
  gameArea.innerHTML = "";

  showScreen(screenGame);
  updateHUD();
  spawnCupcake(); // ← add this line
}
✅ Try it! Click Let's Play! — a cupcake should appear somewhere in the game area! Click the button a few more times and watch it pop up in different spots each time.
👀 Check your code
// ... variables from Step 1 ...

function showScreen(screenToShow) { ... }

function updateHUD() {
  scoreDisplay.textContent = score;
  timerDisplay.textContent = timeLeft;
}

function spawnCupcake() {
  const cupcakes = ["🧁", "🍰", "🎂", "🍩", "🍪"];

  const randomIndex = Math.floor(Math.random() * cupcakes.length);
  const emoji = cupcakes[randomIndex];

  const cupcake = document.createElement("div");
  cupcake.classList.add("cupcake");
  cupcake.textContent = emoji;

  const gameWidth  = gameArea.offsetWidth;
  const gameHeight = gameArea.offsetHeight;

  cupcake.style.left = Math.floor(Math.random() * (gameWidth  - 80)) + "px";
  cupcake.style.top  = Math.floor(Math.random() * (gameHeight - 80)) + "px";

  gameArea.appendChild(cupcake);
}

function startGame() {
  score    = 0;
  timeLeft = 30;
  gameArea.innerHTML = "";

  showScreen(screenGame);
  updateHUD();
  spawnCupcake();
}

btnStart.addEventListener("click", startGame);
Step 5

Click to Score!

Cupcakes are appearing — but clicking them does nothing yet. Let's add an event listener to each cupcake so that clicking it scores a point and makes it disappear.

Inside your spawnCupcake function, add the following after the gameArea.appendChild(cupcake) line:

  cupcake.addEventListener("click", function () {
    score = score + 1;
    updateHUD();
    cupcake.remove();
  });

Your spawnCupcake function should now look like this:

function spawnCupcake() {
  const cupcakes = ["🧁", "🍰", "🎂", "🍩", "🍪"];

  const randomIndex = Math.floor(Math.random() * cupcakes.length);
  const emoji = cupcakes[randomIndex];

  const cupcake = document.createElement("div");
  cupcake.classList.add("cupcake");
  cupcake.textContent = emoji;

  const gameWidth  = gameArea.offsetWidth;
  const gameHeight = gameArea.offsetHeight;

  cupcake.style.left = Math.floor(Math.random() * (gameWidth  - 80)) + "px";
  cupcake.style.top  = Math.floor(Math.random() * (gameHeight - 80)) + "px";

  gameArea.appendChild(cupcake);

  cupcake.addEventListener("click", function () {
    score = score + 1;
    updateHUD();
    cupcake.remove();
  });
}
✅ Try it! Click Let's Play! and then click the cupcake — the score in the HUD should jump to 1 and the cupcake should vanish. Keep clicking Start to spawn more and watch the score climb!
Step 6

They Won't Last Forever

Right now cupcakes sit there patiently waiting forever. That's way too easy! Let's make them disappear after 1.5 seconds if the player doesn't click them in time.

We'll use setTimeout — a built-in JavaScript function that runs some code after a set delay.

⏱️ What is setTimeout?

setTimeout(function, delay) waits for delay milliseconds, then runs the function once. There are 1000 milliseconds in a second — so 1500ms is 1.5 seconds.

Still inside spawnCupcake, add this block after your click event listener:

  setTimeout(function () {
    if (cupcake.isConnected) {
      cupcake.remove();
    }
  }, 1500);
🤔 What does isConnected do?

When the timer fires, we check if the cupcake is still on the page. If the player already clicked it, it was already removed — so isConnected will be false and we skip the remove.

✅ Try it! Click Let's Play! and just… wait. After 1.5 seconds the cupcake should vanish by itself! Now try clicking it before the time's up — the score should still go up as normal.
👀 Check your code — spawnCupcake so far
function spawnCupcake() {
  const cupcakes = ["🧁", "🍰", "🎂", "🍩", "🍪"];

  const randomIndex = Math.floor(Math.random() * cupcakes.length);
  const emoji = cupcakes[randomIndex];

  const cupcake = document.createElement("div");
  cupcake.classList.add("cupcake");
  cupcake.textContent = emoji;

  const gameWidth  = gameArea.offsetWidth;
  const gameHeight = gameArea.offsetHeight;

  cupcake.style.left = Math.floor(Math.random() * (gameWidth  - 80)) + "px";
  cupcake.style.top  = Math.floor(Math.random() * (gameHeight - 80)) + "px";

  gameArea.appendChild(cupcake);

  cupcake.addEventListener("click", function () {
    score = score + 1;
    updateHUD();
    cupcake.remove();
  });

  setTimeout(function () {
    if (cupcake.isConnected) {
      cupcake.remove();
    }
  }, 1500);
}
Step 7

Let's Play!

Right now we can only spawn one cupcake at a time. Let's upgrade startGame to keep spawning cupcakes continuously and count down the clock — using setInterval.

🔁 setInterval vs setTimeout

setTimeout runs once after a delay. setInterval keeps running over and over on a repeating schedule — until you tell it to stop with clearInterval.

Update your startGame function to look like this — the new lines are the two setInterval blocks:

function startGame() {
  score    = 0;
  timeLeft = 30;
  gameArea.innerHTML = "";

  showScreen(screenGame);
  updateHUD();

  // Countdown — ticks every second
  gameTimerInterval = setInterval(function () {
    timeLeft = timeLeft - 1;
    updateHUD();

    if (timeLeft <= 0) {
      endGame();
    }
  }, 1000);

  // Spawner — drops a new cupcake every 800ms
  spawnTimerInterval = setInterval(function () {
    spawnCupcake();
  }, 800);
}

You'll notice we're calling endGame() when the timer hits zero — we'll write that function in the next step!

✅ Try it! Click Let's Play! — cupcakes should start raining down and the timer should count towards zero. Click as many as you can! (The game won't end properly yet — that's next.)
Step 8

Game Over!

The timer hits zero… and nothing happens. Let's fix that! We need an endGame function that stops the timers, shows the end screen, and gives the player a message based on how they did.

Add the following two functions to your code, above startGame:

function getEndMessage(finalScoreValue) {
  if (finalScoreValue >= 20) {
    return "Amazing! You're a cupcake-catching champion! 🏆";
  } else if (finalScoreValue >= 10) {
    return "Great job! You've got quick hands! 👏";
  } else {
    return "Keep practising — those cupcakes are sneaky! 😄";
  }
}

function endGame() {
  clearInterval(gameTimerInterval);
  clearInterval(spawnTimerInterval);

  finalScore.textContent = score;
  endMessage.textContent = getEndMessage(score);

  showScreen(screenEnd);
}
🔀 What is if / else if / else?

A conditional lets your code make decisions. JavaScript checks the first condition — if it's true, it runs that block and stops. If not, it tries the next one. The final else is the fallback if nothing else matched.

Finally, add the Play Again button listener at the bottom alongside your Start button listener:

btnRestart.addEventListener("click", startGame);
✅ Try it! Play a full round! When the timer hits zero the end screen should appear with your score and a personalised message. Hit Play Again to go again. You built a game! 🎉
🎉 Final code — the complete game
// ── Variables ──────────────────────────────────────────────
let score    = 0;
let timeLeft = 30;

let gameTimerInterval  = null;
let spawnTimerInterval = null;

// ── Get elements from the page ──────────────────────────────
const screenStart = document.getElementById("screen-start");
const screenGame  = document.getElementById("screen-game");
const screenEnd   = document.getElementById("screen-end");

const scoreDisplay = document.getElementById("score-display");
const timerDisplay = document.getElementById("timer-display");
const finalScore   = document.getElementById("final-score");
const endMessage   = document.getElementById("end-message");
const gameArea     = document.getElementById("game-area");

const btnStart   = document.getElementById("btn-start");
const btnRestart = document.getElementById("btn-restart");

// ── Functions ───────────────────────────────────────────────
function showScreen(screenToShow) {
  screenStart.classList.add("hidden");
  screenGame.classList.add("hidden");
  screenEnd.classList.add("hidden");
  screenToShow.classList.remove("hidden");
}

function updateHUD() {
  scoreDisplay.textContent = score;
  timerDisplay.textContent = timeLeft;
}

function spawnCupcake() {
  const cupcakes = ["🧁", "🍰", "🎂", "🍩", "🍪"];

  const randomIndex = Math.floor(Math.random() * cupcakes.length);
  const emoji = cupcakes[randomIndex];

  const cupcake = document.createElement("div");
  cupcake.classList.add("cupcake");
  cupcake.textContent = emoji;

  const gameWidth  = gameArea.offsetWidth;
  const gameHeight = gameArea.offsetHeight;

  cupcake.style.left = Math.floor(Math.random() * (gameWidth  - 80)) + "px";
  cupcake.style.top  = Math.floor(Math.random() * (gameHeight - 80)) + "px";

  gameArea.appendChild(cupcake);

  cupcake.addEventListener("click", function () {
    score = score + 1;
    updateHUD();
    cupcake.remove();
  });

  setTimeout(function () {
    if (cupcake.isConnected) {
      cupcake.remove();
    }
  }, 1500);
}

function getEndMessage(finalScoreValue) {
  if (finalScoreValue >= 20) {
    return "Amazing! You're a cupcake-catching champion! 🏆";
  } else if (finalScoreValue >= 10) {
    return "Great job! You've got quick hands! 👏";
  } else {
    return "Keep practising — those cupcakes are sneaky! 😄";
  }
}

function startGame() {
  score    = 0;
  timeLeft = 30;
  gameArea.innerHTML = "";

  showScreen(screenGame);
  updateHUD();

  gameTimerInterval = setInterval(function () {
    timeLeft = timeLeft - 1;
    updateHUD();
    if (timeLeft <= 0) {
      endGame();
    }
  }, 1000);

  spawnTimerInterval = setInterval(function () {
    spawnCupcake();
  }, 800);
}

function endGame() {
  clearInterval(gameTimerInterval);
  clearInterval(spawnTimerInterval);

  finalScore.textContent = score;
  endMessage.textContent = getEndMessage(score);

  showScreen(screenEnd);
}

// ── Event listeners ─────────────────────────────────────────
btnStart.addEventListener("click", startGame);
btnRestart.addEventListener("click", startGame);
🎉

You built a game!

You've used variables, functions, event listeners, conditionals, timers, and DOM manipulation — that's a huge chunk of JavaScript covered in one session. Nice work!