Phaser AE

Snake and maze-snake rules

Free packv3.0.0-32DArcade

The rules of a grid snake game as a configurable session: the snake, its food, its walls and the game around it, from a classic endless board to a run of timed maze waves. Board size, wrap, obstacles, food, speed, timer, waves, lives and scoring are all configuration.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 20, 2026

Snake and maze-snake rules: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

snake

function snake(): Pack<{ snake: typeof api }>

Install the renderer-independent Snake/Nibbler rules API as game.snake.

Helpers

createSnake

function createSnake(config: SnakeConfig)

Create an independent Snake board; a SnakeGame or the host chooses step timing.

createSnakeGame

function createSnakeGame(config: SnakeGameConfig)

Create a Snake/Nibbler session.

DEFAULT_SNAKE_LEGEND

const DEFAULT_SNAKE_LEGEND: SnakeMazeLegend

# wall, . food, space empty, > < ^ v the head and its heading.

NIBBLER_WAVES

const NIBBLER_WAVES: readonly SnakeWave[]

Six authored 31×21 Nibbler-style waves. Rock-Ola's 32 original maze layouts are not published as data, so these are credible approximations built on the documented structure: one-cell corridors, no dead ends (a halted snake can always be steered on), waves 3 and 6 are open fields bounded only by the outer wall, and the cycle repeats faster (wave 7 is wave 1 at a higher speed).

parseSnakeMaze

function parseSnakeMaze( maze: readonly string[], legend: SnakeMazeLegend = DEFAULT_SNAKE_LEGEND, ): Partial<SnakeConfig> & { cols: number; rows: number }

Parse an ASCII maze into board fields: cols, rows, obstacles, foods and, when a head marker (> < ^ v) is present, start and direction. The body trails straight behind the head for startLength cells, shortened at a wall. Unknown characters are an error.

snakeGamePreset

function snakeGamePreset( kind: SnakeGameKind = "classic", options: SnakeGameOptions = {}, ): SnakeGameConfig

A complete session preset. classic: walled board, one food at a time, speed rising per food to a cap, one life, play until you crash or fill the board. wrap: the same with wrapping edges and three foods. nibbler: Rock-Ola 1982 rules (see the pack docs). Every field can be overridden; board overrides merge into the preset board.

snakeObstacles

function snakeObstacles(layout: SnakeObstacleLayout, cols: number, rows: number): Cell[]

Obstacle cells for a cols × rows board: box an inner border with a gap in the middle of each side, pillars 2×2 blocks on a regular lattice, bars two horizontal bars at the quarter heights with a central gap. The centre row (floor(rows/2), where the default start lies) is always kept clear. Small boards simply get fewer cells.

snakePreset

function snakePreset( kind: "classic" | "orchard" | "maze" = "classic", options: Partial<SnakeConfig> = {}, ): SnakeConfig

Public authored board presets; dimensions and every rule remain overrideable.

Classes

Snake

class Snake

Owns grid growth, food placement, turn buffering, wall policy and collision/win rules. The host (or a SnakeGame session) decides when to call step().

  • readonly cols: number — Board width in cells.
  • readonly rows: number — Board height in cells.
  • readonly config: SnakeConfig — A copy of the configuration the board was built from.
  • constructor(cfg: SnakeConfig)
  • get state() — Copied serialisable state.
  • get body() — Legacy mutable body array (head first); prefer state.body.
  • get food(): Cell — First food, or {-1,-1} when none remains.
  • get foods() — A copy of the foods on the board.
  • get dead() — Whether the snake has died.
  • get won() — Whether the goal has been met.
  • get score() — Points scored on this board.
  • get eaten() — Food eaten on this board.
  • get stalled() — Consecutive steps halted against a wall (stop policy).
  • get direction() — Current heading (the direction of the last move, or the initial heading).
  • isObstacle(p: Cell) — True when p is a wall/obstacle cell.
  • next(from: Cell, d: Direction): Cell — The cell one step from from in d, wrapped when wrap is on; may be off-board otherwise.
  • input(d: Direction): boolean — Buffer a turn. Up to queueSize turns are remembered and applied one per step in the order pressed; each is checked against the LAST queued heading, so up-then-left inside one move works while any reversal into the neck is refused. Returns whether the turn was accepted.
  • step(): SnakeStep — Advance one cell. Food, growth, wall policy, collisions and outcome are resolved here.
  • autoInput() — Safe one-step steering helper; useful for an optional bot, not a claimed solver.
  • snapshot() — A copy of the serialisable state, to save; the same as state.
  • restore(s: SnakeState) — Replaces the state with a saved one from snapshot(). Throws if the body, foods, queue, counters or status are invalid, or the queue is longer than queueSize.
  • reset() — Rebuilds the board from its configuration, as at construction: the starting body, foods and seed, with the score and counters at zero.

SnakeGame

class SnakeGame

A complete Snake/Nibbler session around a Snake board. It owns every rule above the board: the title/ready/playing/paused/dying/clear/over phases, lives and extra lives, wave progression and looping, the wave timer and its stall acceleration, the speed curve and cap, score multipliers, time bonus and the high score. The host only calls update(dt), forwards turns with input(), start() and pause(), and draws state.

  • readonly config: SnakeGameConfig — The session configuration: a copy of what the constructor was given.
  • constructor(config: SnakeGameConfig)
  • indexFor(n: number) — Index of the authored wave played as wave n.
  • waveFor(n: number): SnakeWave | undefined — The authored wave played as wave n, if any.
  • boardFor(n: number, game = this.s?.games ?? 0): SnakeConfig — The full board config for wave n: base board, then the wave's maze, then its overrides.
  • limitFor(n: number) — Time limit in seconds for wave n (Infinity without a timer).
  • speedFor(n: number, eaten: number) — Cells per second for the current wave and eaten count, capped.
  • get board(): Snake — The live board (rules for movement, food and collisions).
  • get state(): SnakeGameState — Copied session state, including the board snapshot.
  • get phase() — The session's lifecycle phase.
  • start(): boolean — Begin a new game from title, game over or a won game; ignored mid-game.
  • newGame() — Begin a new game unconditionally (keeps the high score).
  • title() — Return to the title phase showing the first wave (keeps the high score).
  • pause(on?: boolean): boolean — Pause or resume (toggle when on is omitted). Only ready/playing can pause.
  • input(d: Direction): boolean — Buffer a turn; accepted during READY too, so a pre-pressed turn applies on the first move.
  • update(dt: number): SnakeGameEvent[] — Advance the session by dt seconds (clamped to 0.25 per call). Returns what happened.
  • snapshot(): SnakeGameState — Serializable copy of the whole session.
  • restore(state: SnakeGameState) — Restore a snapshot taken from a session with the same config.

Types

SnakeConfig

interface SnakeConfig

Dimensions, body/obstacles/foods, turn buffering, wrap, wall and growth/objective policies.

  • cols: number — Board width in cells; a whole number of at least 1. Width times height may not exceed one million cells.
  • rows: number — Board height in cells; a whole number of at least 1. Width times height may not exceed one million cells.
  • seed?: number — Seed for food placement.
  • wrap?: boolean — Edges join to the opposite side instead of being walls.
  • start?: Cell — Head cell when body is not authored. Defaults to the board centre.
  • body?: Cell[] — Authored body, head first, each cell adjacent to the previous one.
  • startLength?: number — Length of the generated straight body behind start when body is absent (default 3).
  • direction?: Direction — Initial heading (default right).
  • obstacles?: Cell[] — Solid cells.
  • foods?: SnakeFood[] — Authored foods. When given, no random food is placed at construction.
  • foodCount?: number — Random foods kept on the board while respawnFood is on (default 1).
  • foodGrowth?: number — Segments added per food unless the food overrides it (default 1).
  • foodScore?: number — Points per food unless the food overrides it (default 10).
  • respawnFood?: boolean — Replace eaten food with a random free cell (default true; ignored for clear-food).
  • goal?: "fill" | "clear-food" | "score" — Win condition besides filling the board: fill (default), clear-food or score.
  • targetScore?: number — Score needed for the score goal (default 100).
  • queueSize?: number — Buffered turns (default 3). Rapid key presses inside one move are honoured in order.
  • wallPolicy?: SnakeWallPolicy — Wall/obstacle/edge behaviour (default die).
  • autoTurn?: boolean — With stop, follow single-exit corners without input (default true for stop).

SnakeDeath

type SnakeDeath = "wall" | "obstacle" | "self"

Why a snake died.

SnakeFood

interface SnakeFood extends Cell

An authored food cell with optional appearance kind, growth and score overrides.

  • kind?: string — Appearance/category tag for the renderer; rules ignore it. Defaults to food.
  • growth?: number — Segments added when eaten; defaults to foodGrowth.
  • score?: number — Base points when eaten; defaults to foodScore. A session may multiply it.

SnakeGameConfig

interface SnakeGameConfig

Session rules around a Snake board: waves, lives, timer, speed curve, scoring and banners.

  • board: SnakeConfig — Base board rules for every wave (a wave's maze and board override it).
  • waves?: SnakeWave[] — Authored waves. Absent: one endless board (classic Snake).
  • loopWaves?: boolean — After the last wave, repeat from the first with the wave number (speed, timer, multiplier) still rising (default true).
  • lives?: number — Lives at the start (default 3).
  • maxLives?: number — Life cap (default 99).
  • extraLifeEveryWaves?: number — Award a life after clearing every Nth wave (0 = never).
  • extraLifeScores?: number[] — Award a life when the score first reaches each of these totals.
  • speed?: SnakeSpeed — Movement speed curve (default 8 cells/s flat).
  • timer?: SnakeTimer | null — Wave countdown, or null for none.
  • scoreMultiplier?: "none" | "wave" — wave multiplies food and time-bonus points by the wave number (Nibbler); none (default) does not.
  • keepEatenFood?: boolean — After a death, keep the food already eaten on authored (clear-food) boards (default true).
  • readyTime?: number — Seconds of READY before moving (default 1).
  • deathTime?: number — Seconds the death banner shows (default 1.2).
  • clearTime?: number — Seconds the wave-clear banner shows (default 2).
  • highScore?: number — Initial high score (default 0).
  • startWave?: number — First wave number (default 1).
  • legend?: SnakeMazeLegend — Maze legend; defaults to DEFAULT_SNAKE_LEGEND.

SnakeGameEvent

type SnakeGameEvent = | { type: "go" } | { type: "move" } | { type: "blocked" } | { type: "ate"; food: SnakeFood; points: number } | { type: "died"; cause: SnakeLifeLoss } | { type: "cleared"; wave: number; bonus: number } | { type: "wave"; wave: number } | { type: "extra-life"; lives: number } | { type: "game-over" } | { type: "won" }

Something that happened during SnakeGame.update.

SnakeGameKind

type SnakeGameKind = "classic" | "wrap" | "nibbler"

Named session presets.

SnakeGameOptions

type SnakeGameOptions = Omit<Partial<SnakeGameConfig>, "board"> & { board?: Partial<SnakeConfig>; }

Options accepted by snakeGamePreset: any session field, with partial board overrides.

SnakeGamePhase

type SnakeGamePhase = | "title" | "ready" | "playing" | "paused" | "dying" | "clear" | "over" | "won"

Lifecycle of a SnakeGame.

SnakeGameState

interface SnakeGameState

Copied, serialisable session state.

  • phase: SnakeGamePhase — The session's lifecycle phase; title before the first game starts.
  • phaseTime: number — Seconds spent in the current phase.
  • resume: SnakeGamePhase — Phase to return to when unpaused.
  • wave: number — 1-based wave number (keeps rising when waves loop).
  • waveIndex: number — Index into waves (0 without waves).
  • lives: number — Lives left, including the one in play.
  • score: number — Points scored this game.
  • highScore: number — Best score so far across games in this session; starts at the configured highScore.
  • timeLeft: number — Seconds left on the wave timer (Infinity without a timer).
  • timeLimit: number — This wave's time limit in seconds (Infinity without a timer).
  • speed: number — Current cells per second.
  • carry: number — Fractional progress towards the next move.
  • stall: number — Seconds the snake has been halted against a wall.
  • bonus: number — Bonus scored by the last wave clear.
  • cause?: SnakeLifeLoss — Cause of the last lost life.
  • eatenTotal: number — Food eaten in this game.
  • awarded: number — Score thresholds already rewarded.
  • games: number — Games started by this session; varies the food seed so each new game differs.
  • board: SnakeState — Snapshot of the current board's state.

SnakeLifeLoss

type SnakeLifeLoss = SnakeDeath | "time"

Why a life was lost: a board death or the wave timer running out.

SnakeMazeLegend

type SnakeMazeLegend = Record<string, "wall" | "empty" | Omit<SnakeFood, "x" | "y">>

Character legend for parseSnakeMaze: a wall, an empty cell or a food template.

SnakeObstacleLayout

type SnakeObstacleLayout = "none" | "box" | "pillars" | "bars"

Named obstacle layouts for open boards.

SnakeSpeed

interface SnakeSpeed

Movement rate in cells per second: start + perWave·(wave−1) + perFood·eaten, capped at max.

  • start: number — Cells per second on wave 1 before anything is eaten.
  • perFood?: number — Added per food eaten on the current board (classic speed-up).
  • perWave?: number — Added per wave after the first (Nibbler speed-up).
  • max: number — Hard cap in cells per second.

SnakeState

interface SnakeState

Serializable body, foods, turn queue, pending growth, the food stream's kernel state and terminal outcome.

  • body: Cell[] — Cells the snake occupies, head first.
  • foods: SnakeFood[] — Foods currently on the board.
  • direction: Direction — Heading of the last move, or the initial heading before the first.
  • queue: Direction[] — Buffered turns not yet applied, oldest first; at most queueSize long.
  • growth: number — Segments still to be added: the tail stays put for this many more steps.
  • score: number — Points scored on this board.
  • eaten: number — Foods eaten on this board.
  • status: "playing" | "won" | "lost" — playing while the round runs, won once the goal is met, lost after a fatal move.
  • random: number[] — The food stream: four words of the kernel's createRng state, advanced in place as food is placed.
  • ticks: number — Steps taken, including steps spent halted against a wall.
  • stalled?: number — Consecutive steps spent halted against a wall (stop policy).
  • held?: boolean — The queue head was refused by a wall and is being held for the next opening (stop policy).
  • cause?: SnakeDeath — Cause of the loss, when lost.

SnakeStep

interface SnakeStep

Result of one Snake.step.

  • moved: boolean — Whether the head advanced a cell this step.
  • ate: boolean — Whether the head moved onto a food this step.
  • dead: boolean — Whether the snake died this step.
  • won: boolean — Whether the goal was met this step.
  • blocked?: boolean — The snake halted against a wall (stop policy).
  • food?: SnakeFood — The food eaten this step.
  • cause?: SnakeDeath — Cause of death this step.

SnakeTimer

interface SnakeTimer

Per-wave countdown. Running out costs a life. limit = max(min, seconds + perWave·(wave−1)) unless the wave authors its own time. Seconds left when a wave is cleared score bonusPerSecond × multiplier each. While the snake is halted against a wall for more than stallGrace seconds the clock speeds up, doubling every stallDoubling seconds (0 disables).

  • seconds: number — Time limit of wave 1, in seconds; at least 0.1.
  • perWave?: number — Seconds added to the limit for each wave after the first; negative values shorten it. Default 0.
  • min?: number — Lowest limit the per-wave change may reach, in seconds. Default 0.1.
  • bonusPerSecond?: number — Points per whole second left when a wave is cleared, before the wave multiplier. Default 0.
  • stallGrace?: number — Seconds halted against a wall before the clock starts running faster. Default 0.
  • stallDoubling?: number — Seconds of further stalling over which the clock's rate doubles, up to 64 times normal; 0 (default) disables the acceleration.

SnakeWallPolicy

type SnakeWallPolicy = "die" | "stop"

What happens when the head meets a wall, obstacle or (without wrap) the board edge.

  • die: classic Snake. The move is fatal.
  • stop: Nibbler. The snake halts in place (time keeps running) and, with autoTurn, follows a corridor round a corner that has exactly one way on. Biting its own body is always fatal.

SnakeWave

interface SnakeWave

One authored wave: an ASCII maze and/or board overrides, plus optional time/speed overrides.

  • name?: string — Display name.
  • maze?: string[] — ASCII maze, parsed with parseSnakeMaze and the session's legend.
  • board?: Partial<SnakeConfig> — Board overrides applied after the maze.
  • time?: number — Time limit in seconds for this wave.
  • speed?: number — Starting speed for this wave (replaces speed.start; per-wave/food increments still apply).