Step 6 of 7

Game state and score

Direct answer: Keep score, lives, and the current game status in a small state object so restart and game-over behavior stay understandable.

What you need first

Input and collision basics. You should already be able to move something and notice when two rectangles overlap.

After this lesson

You can pause a run, end it, restart without leftover positions, and keep a high score after the tab is closed.

One object owns the facts

Score, pause, and game-over are not drawing details. They are facts the rest of the game must agree on. Put them in one place:

const game = {
    status: 'ready', // ready | playing | paused | game-over
    score: 0,
    best: Number(localStorage.getItem('learn-state-best') || 0)
};

function start() {
    game.status = 'playing';
    game.score = 0;
    player.x = 40;
    player.y = 90;
}

function endGame() {
    game.status = 'game-over';
    game.best = Math.max(game.best, game.score);
    localStorage.setItem('learn-state-best', String(game.best));
}

Every update then starts with the same question: is the game playing? If it is paused or over, skip movement. If it is ready, wait for the first input.

When to use it

Use explicit states such as playing, paused, and game-over. Do not let unrelated flags decide the same behavior. A boolean named dead plus another named showOverlay will drift apart the first time you add Restart.

Try it and check it

Collect the gold squares. Press P to pause, then Restart after a miss. The score must return to 0, the player must return to the left, and the best score must survive a refresh.

Status: ready

Restart must reset memory, not only the picture

Clearing the canvas does not restart the game. If the old x, leftover coins, or previous score stay in memory, the next run starts dirty. A restart function should rebuild the same values start() uses.

That is why the Snake project rebuilds the body, direction, food, and score together. The shipped Snake remake does the same, then writes best scores to localStorage under snake_best_v3.

Save only what should outlive the tab

localStorage is for facts the player expects to keep: a high score, a mute setting, a daily seed. Do not save the live snake body there unless you are building a resume feature. Wrap reads in try/catch; private mode and blocked storage should not crash the game.

function loadBest() {
    try {
        return Number(localStorage.getItem('learn-state-best') || 0);
    } catch (error) {
        return 0;
    }
}

Common mistake

Resetting the display but leaving old positions or score values in memory. A second mistake is writing the high score on every frame instead of once when the run ends.

Real game connection

Use the state checklist in the Snake project, then compare it with the remake’s pause button and best-score panel. The remake also has Endless, Blitz, and Daily modes. Those are extra statuses, not a reason to skip this smaller object.

Compatibility: Test in a current Chrome, Firefox, Safari, or Edge browser. Canvas and standard input work broadly in current browsers. localStorage can be blocked; the demo still plays if the save fails.

Source and update: Reviewed against MDN Web Docs for localStorage. Updated 2026-08-13. This page does not claim performance results beyond the local example check.

Next actions: build the Snake project · play Snake · browse reusable code