Game loops and state machines
The pause that did not pause
Kai and Remy's first Hyper Shards had a strange bug. With the pause menu open, pressing fire still used up laser energy. And pressing pause on the game over screen started the music again.
The code kept a pile of true-or-false switches: isPaused, isGameOver, onTitle. Every function checked some of them and forgot others.
This lesson is about the shape underneath every game: the game loop, and state machines that keep a game in exactly one mode at a time.
The game loop
A normal app waits for you to click. A game cannot wait: aliens keep flying even if you do nothing. So every game runs a game loop, three jobs repeated every frame, many times a second:
- 1. InputRead what the player is doing right now: keys held, taps, the gamepad.
- 2. UpdateMove ships and bullets, check for hits, add points, spawn the next aliens.
- 3. RenderDraw everything where it now is. Rendering just means drawing.
Bolt says: In a browser, requestAnimationFrame(loop) asks the browser to run loop just before it next repaints the screen, usually at the screen's refresh rate. It runs only once, so loop asks again at the end of every frame. PixiJS runs a loop like this for us, called the ticker.
Quick check
In one frame of Hyper Shards, the player presses fire. In which order does the loop handle it?
- Read the press, create and move the laser, then draw it
- Draw the laser, then read the press, then move it
- Move the laser, draw it, then read the press a second later
Show the answer
Read the press, create and move the laser, then draw it
Input comes first so the update can react to it in the same frame, and drawing comes last so the picture shows the newest positions. Any other order makes the game feel a frame late.
Game state and drawing, kept apart
Game state is all the data that describes the game right now: positions, health, the score, the wave and which screen is showing. Update changes the state. Render only reads it and paints it, and never changes it.
Keeping them apart pays off. Pausing means "skip update, keep rendering". Saving means saving the state. Update can even be tested with no screen at all.
Update moves things by speed * dt, where dt is the seconds since the last frame (the game math lesson shows why). Some games update in fixed steps of exactly 1/60 of a second, so physics comes out the same on every device.
Bolt says: How long a frame may take, the 16.7 millisecond budget, is in the 60 frames per second lesson.
Finite state machines
A finite state machine (FSM) has a fixed list of states. It is always in exactly one of them, and it moves between them only through set transitions, each started by an event. Hyper Shards' screens form one with four states:
- title goes to playing when you press Play.
- playing goes to paused on the pause key, or to gameOver when the ship is destroyed.
- paused goes back to playing on Resume, or to title on Quit.
- gameOver goes to playing on Retry, or to title.
In code, that is one variable: let state = 'title';
One variable beats many switches
Three true-or-false switches make 2 x 2 x 2 = 8 combinations, but only 4 of them make sense. The other 4, like paused and game over at once, are bugs waiting to happen. One state variable can only hold one value, so those impossible mixes can no longer exist.
A state machine also decides which inputs count. While state is 'paused', the code for the fire key is never even asked. Robert Nystrom's book Game Programming Patterns shows the same fix for a hero who could jump again in mid-air.
Bug hunt
1let state = 'title';2function update(dt) {3 if (input.fire) fireLaser();4 if (state === 'playing') {5 moveAliens(dt);6 moveLasers(dt);7 }8}9function onPauseKey() {10 if (state === 'playing') state = 'paused';11 else if (state === 'paused') state = 'playing';12}
Testers say pressing fire on the pause menu still uses up laser energy. Which line is the bug?
- Line 1
- Line 3
- Line 4
- Line 10
Show the answer
Line 3
Line 3 reads the fire key before checking the state, so it runs on every screen. Moving it inside the playing block that starts on line 4 means only the playing state listens for fire.
Characters have states too
The same idea works for one character. Sprig, the hero of Garden Defense, has four states: idle, walk, jump and hurt. Each state picks Sprig's animation, says which buttons count, and says how to leave:
- idle and walk can start a jump.
- jump ends when Sprig lands.
- hurt lasts 1 second, with Sprig blinking and safe from more hits, then goes back to idle.
Enemies get state machines too, like a beetle that wanders, chases and runs away. The enemy brains lesson builds on this one.
Read the code
1function onJumpPressed() {2 const s = sprig.state;3 if (s === 'idle' || s === 'walk') {4 sprig.state = 'jump';5 sprig.vy = -500;6 }7}8function onHit() {9 if (sprig.state === 'hurt') return; // safe10 sprig.state = 'hurt';11 sprig.hurtTimer = 1; // seconds12}
A beetle hits Sprig. Half a second later, the player presses jump. What happens?
- Nothing: Sprig is in the hurt state, and only idle or walk can start a jump
- Sprig jumps, because the jump button always works
- Sprig gets hurt a second time, because the beetle is still there
- The game crashes, because Sprig is in two states at once
Show the answer
Nothing: Sprig is in the hurt state, and only idle or walk can start a jump
For one second Sprig's state is hurt. onJumpPressed only allows a jump from idle or walk, and onHit returns early while Sprig is hurt. A state variable can never hold two states, so there is nothing to crash.
Systems, modules and ECS
As a game grows, update splits into systems, each doing one job for the whole game every frame. Hyper Shards runs input, movement, collisions, scoring and drawing, each in its own module, a file with one job (see Code like LEGO in Stage 2).
Some games go further with ECS, entity component system. An entity is just an ID number. Components are plain data stuck onto it, like position, velocity or health. A system works on every entity that has the components it needs: movement moves anything with a position and a velocity: ship, laser or shard.
Bolt says: Ask Claude Opus 5.5 to list your game's states and transitions before it writes any code. Next lesson: controls that feel fast and fair, even when the player presses a little early.
Fact-checked 9 October 2026
- Game Programming Patterns: game loop gameprogrammingpatterns.com
- Game Programming Patterns: state gameprogrammingpatterns.com
- MDN: requestAnimationFrame developer.mozilla.org