CognitoCoding All posts
10 July 2026 4 min read by Eris Taylor

How I Built Pac-Man in Scratch — And What It Taught Me About State Machines

scratchgame-devstate-machinesbehind-the-buildbeginners

Four ghosts. Three states each. One state machine I had to build from scratch (literally).

When I started the Pac-Man project, I thought the hard part would be the maze or the pellets. It wasn't. The hard part was making ghosts that feel intelligent — ghosts that chase, retreat, scatter, and panic — without any actual AI.

The answer was a state machine. And once I understood why it worked, I saw it everywhere.

Watch the full build: Scratch Retro Pac-Man Deep Dive


What the Ghosts Actually Do

In classic Pac-Man, each ghost runs through three behavioural states:

  1. Chase — the ghost hunts Pac-Man, moving towards his position
  2. Scatter — the ghost retreats to its home corner and loops around it (this is what makes the game survivable — it gives you breathing room)
  3. Frightened — Pac-Man ate a power pellet; the ghost turns blue and tries to avoid him

The game switches between Chase and Scatter on a fixed timer. Frightened kicks in on power pellet events and overrides everything else until it wears off.

That's a state machine. Three named states, rules for moving between them, and each state driving completely different behaviour.


Why a State Machine? Not Just an If Chain

You could write ghost behaviour as a pile of if statements. Many beginners do. It gets messy fast:

# Fragile — grows unreadable quickly
if chasing and not frightened:
    move_towards_pacman()
elif not chasing and not frightened:
    move_to_corner()
elif frightened:
    move_away_from_pacman()

The problem: every condition has to check multiple things at once. Add a fourth state and you're rewriting half the logic. Bugs hide in the overlaps.

A state machine gives each state a name, owns its own logic, and uses clearly defined transitions. Here's the stripped-down version:

ghost_state = "chase"

def update_ghost():
    if ghost_state == "chase":
        move_towards_pacman()
    elif ghost_state == "scatter":
        move_to_corner()
    elif ghost_state == "frightened":
        move_away_from_pacman()

def on_power_pellet():
    ghost_state = "frightened"

def on_frightened_timer_end():
    ghost_state = "chase"

def on_scatter_timer():
    ghost_state = "scatter"

Each state has one job. Transitions happen in response to events. You can read this code and understand every ghost's entire life in under a minute.


How I Built This in Scratch

Scratch doesn't have Python-style state strings — you work with sprites, variables, and blocks. So I used a variable called ghost_mode (1 = chase, 2 = scatter, 3 = frightened) and drove each ghost sprite's movement blocks from that single number.

When a "Power Pellet Eaten" message was broadcast, every ghost received it and immediately set their ghost_mode to 3. Their movement blocks switched from "point towards Pac-Man" to "point away from Pac-Man". When the frightened timer hit zero, another broadcast flipped them back to 1.

The timer itself lived in a separate "Game Manager" sprite — watching the clock and firing the right broadcasts at the right moments. That sprite was the machine. The ghosts were just responding to it.

Same pattern as the pseudocode above. Different blocks.


Where This Pattern Shows Up in Real Games

Once you see state machines, you find them everywhere:

  • Enemy AI in RPGs — idle → patrol → alert → attack → retreat
  • Menu systems — main menu → options → gameplay → pause → game over
  • Animation rigs — idle → walk → run → jump → fall → land
  • Login flows — unauthenticated → authenticating → authenticated → session expired

Every piece of behaviour that switches between modes is a candidate for a state machine. It's one of the most transferable patterns in software — you learn it in Scratch building a ghost, and you find it again writing Unity, Godot, or a React app.


What I'd Do Differently

Looking back at the build, one thing I'd change: I'd give each ghost individual state, not shared state. In the version in the video, a power pellet flips all four ghosts to frightened at once — which is correct for Pac-Man. But if you wanted ghosts with different personalities (one stays in scatter longer, one recovers from frightened faster), you'd need each ghost to own its own ghost_mode variable.

That's the next level of state machines: stateful objects. Each ghost becomes its own little machine rather than reacting to a shared broadcast.


Watch the Full Build

The full Pac-Man episode walks through the maze construction, the pellet counter, the ghost AI, and the moments where things went wrong and needed fixing. Worth watching end to end if you want to see the decisions play out live.

Scratch Retro Pac-Man Deep Dive — watch now

The Scratch project link is in the video description. Remix it. Change the ghost speeds, add a fourth state, break the frightened timer and see what happens. That's the fastest way to really get how this works.