Lesson 1

What is an Idle Game?

๐Ÿ“– 5 min read
By the end of this lesson, you will be able to:
  • Define the core loop of an idle game
  • Recognize idle game patterns in popular titles
  • Plan the entities for your first idle game

Definition

An idle game (also called clicker or incremental game) is a game where the player performs a simple action โ€” usually clicking โ€” to accumulate a resource, then spends that resource on upgrades that produce more resource over time, often automatically.

Examples

  • Cookie Clicker (2013) โ€” the genre-defining game. Click cookie, buy grandmas, grandmas bake cookies.
  • AdVenture Capitalist โ€” money instead of cookies, businesses instead of grandmas.
  • Universal Paperclips โ€” start clicking to make paperclips, end up consuming the universe.

Why idle games are perfect first projects

  1. The core loop is tiny โ€” under 50 lines of code
  2. No physics, no animation, no asset generation needed
  3. All the interesting design is in the numbers (rates, costs, multipliers)
  4. You can ship a complete game in 30 minutes

Plan your entities

For our cookie clicker, we need:

  • Score โ€” number of cookies the player owns
  • Cookie button โ€” when clicked, adds 1 to score
  • Upgrades โ€” items the player buys with cookies that auto-generate more cookies
  • Game loop โ€” runs ~10 times per second, adds auto-generated cookies to score
Exercise 1.1
Pick a theme for your idle game. It doesn't have to be cookies. Some ideas: planets being terraformed, fish swimming in a tank, vampires bitten, books written. Choose what entities you'll have for the click target and 3-5 upgrade types.
Lesson 2

The Clicker Loop

๐Ÿ“– 7 min read
By the end of this lesson, you will be able to:
  • Build a single-file HTML game with score tracking
  • Use setInterval for a tick-based game loop
  • Update the UI on every state change

The minimum viable clicker (12 lines)

<h1>๐Ÿช <span id="score">0</span></h1>
<button id="btn">Click me</button>
<script>
let score = 0;
const scoreEl = document.getElementById('score');
const btn = document.getElementById('btn');

btn.addEventListener('click', () => {
  score++;
  scoreEl.textContent = score;
});
</script>

This is a complete working clicker. Save it as index.html and open it in any browser. Click the button โ€” the number goes up.

The game loop

To add auto-generation, run a function repeatedly using setInterval:

let cookiesPerSecond = 0;

setInterval(() => {
  score += cookiesPerSecond / 10;  // divide by 10 since we tick 10x/sec
  render();
}, 100);  // every 100ms = 10 ticks per second

Why 10 ticks per second instead of 1? Smoother number animation. The player sees the score climbing continuously instead of jumping by integers.

Rendering pattern: render-from-state

Don't update the UI in 12 different places. Have one function that reads the current state and updates the UI:

function render() {
  document.getElementById('score').textContent = Math.floor(score);
  document.getElementById('per-sec').textContent = cookiesPerSecond.toFixed(1);
  // ... update all other UI elements
}

Then call render() after any state change. The UI never gets out of sync with the data.

This pattern scales. "State changes โ†’ render from state" is the same pattern used by React, Vue, and every modern UI framework. You're learning the foundation by doing it with plain JavaScript.
Lesson 3

Upgrades & Exponential Cost Scaling

๐Ÿ“– 8 min read
By the end of this lesson, you will be able to:
  • Implement purchasable upgrades that affect the game loop
  • Apply exponential cost scaling for balanced progression
  • Understand why idle games feel addictive (it's the math)

Defining upgrades as data

const upgrades = [
  { name: 'Auto-clicker',  baseCost: 15,   perSec: 0.1 },
  { name: 'Grandma',       baseCost: 100,  perSec: 1   },
  { name: 'Factory',       baseCost: 500,  perSec: 8   },
  { name: 'Bank',          baseCost: 5000, perSec: 47  },
];

let owned = upgrades.map(() => 0);

Exponential cost scaling

The classic idle-game formula: each upgrade purchased increases the next purchase cost by 15%:

function costFor(upgradeIndex) {
  const u = upgrades[upgradeIndex];
  return Math.floor(u.baseCost * Math.pow(1.15, owned[upgradeIndex]));
}

Why 1.15? Empirically tuned to keep the player buying every ~30 seconds. Larger ratios (1.5, 2.0) feel grindy. Smaller (1.05) lets the player snowball too fast.

Owned countCost (base 100, ratio 1.15)
0100
5201
10405
201,637
50108,366

Computing total cookies-per-second

function perSec() {
  return upgrades.reduce((sum, u, i) => sum + u.perSec * owned[i], 0);
}

Buy logic

function buy(upgradeIndex) {
  const cost = costFor(upgradeIndex);
  if (score >= cost) {
    score -= cost;
    owned[upgradeIndex]++;
    render();
  }
}

The addiction loop

Why do idle games hook players? The math: every upgrade purchase increases per-second income, which means the next upgrade is reachable faster, which gives a dopamine hit, which makes the next upgrade reachable even faster. The exponential cost prevents this from breaking โ€” it always feels like you're almost there.

Exercise 3.1
Play the live Cookie Clicker template. Notice how long it takes to afford the first Grandma, then Factory, then Bank. Tune the ratio in your own version to make it more or less grindy.
Lesson 4

Polish & Save Progress

๐Ÿ“– 8 min read
By the end of this lesson, you will be able to:
  • Add visual feedback to clicks (floating "+1" popups)
  • Save and load game state with localStorage
  • Calculate offline progress (cookies earned while away)
  • Identify next-level features to add

Click feedback popups

btn.addEventListener('click', (e) => {
  score += 1;

  const popup = document.createElement('div');
  popup.className = 'popup';
  popup.textContent = '+1';
  popup.style.left = e.clientX + 'px';
  popup.style.top  = e.clientY + 'px';
  document.body.appendChild(popup);
  setTimeout(() => popup.remove(), 1000);

  render();
});

The popup is styled with CSS to float upward and fade out:

.popup {
  position: fixed;
  pointer-events: none;
  font-size: 24px;
  font-weight: 900;
  color: #de6262;
  animation: float 1s ease-out forwards;
}
@keyframes float {
  to { transform: translateY(-80px); opacity: 0; }
}

Saving game state

function save() {
  localStorage.setItem('clicker_save', JSON.stringify({
    score: score,
    owned: owned,
    savedAt: Date.now()
  }));
}

function load() {
  const data = JSON.parse(localStorage.getItem('clicker_save') || 'null');
  if (data) {
    score = data.score;
    owned = data.owned;
    return data.savedAt;
  }
  return null;
}

// Auto-save every 5 seconds
setInterval(save, 5000);

// Load on page open
window.addEventListener('DOMContentLoaded', () => {
  load();
  render();
});

Offline progress

Calculate how many cookies the player would have earned while away:

const savedAt = load();
if (savedAt) {
  const secondsAway = (Date.now() - savedAt) / 1000;
  const earned = Math.floor(perSec() * secondsAway);
  if (earned > 0) {
    score += earned;
    alert(`Welcome back! You earned ${earned} cookies while away.`);
  }
}

Features to add next

  • Achievements โ€” "First grandma!", "100 clicks!", "1 million cookies!"
  • Click multipliers โ€” temporary boosts (golden cookies that randomly appear)
  • Prestige system โ€” reset for a permanent multiplier (extends gameplay infinitely)
  • Sound effects โ€” soft click on cookie press
  • Visual upgrades โ€” the cookie gets bigger or fancier as you progress
You shipped a real game. Take the URL of your deployed cookie clicker and send it to 3 friends. Watch what they do without explaining. Their first 30 seconds tells you everything.
Final exercise
Add one of the "features to add next" to your game. Pick the simplest one (achievements). Ship it. Now you've done iteration #2 โ€” the hardest part of any project.

๐ŸŽ“ Course complete

Ready for a bigger project? Try the Monster Truck Game course next.

Next: Monster Truck Game โ†’ Browse all courses