Phaser AE

Swallow-and-grow arena

Pro packv3.0.0-32DArcade

A swallow-and-grow game for one stick: a hole roams a city, swallows whatever fits and widens as it scores, until buses and towers drop in and smaller holes are prey. The city, what is in it and how fast holes grow are all data, and the pack hands you the state to draw.

This is a Pro pack. Upgrade to Phaser Pro to use it in your games.See Phaser Pro →

Released October 14, 2026 · updated October 20, 2026

Swallow-and-grow arena: 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

consumeGrow

function consumeGrow(): Pack<{ consumeGrow: ConsumeGrowApi }>

The consume-grow pack: game.consumeGrow, Hole.io's rules. No options.

Helpers

cityKinds

const cityKinds: ThingKind[]

The built-in kinds.

cityLayout

const cityLayout: Required<CityOptions>

The built-in layout.

createConsumeGrow

function createConsumeGrow(options: HoleOptions = {}): ConsumeGrowGame

Start a round. Throws, with every fault, on options validateConsumeGrow refuses.

createHoleAutopilot

function createHoleAutopilot(game: ConsumeGrowGame, id = 'you'): HoleAutopilot

The player's autopilot. It only reads game.

holeNames

const holeNames: string[]

Names for rival holes.

pathPoint

function pathPoint(p: ThingPath): { x: number; y: number; angle: number }

Where a thing on a path is, and which way it faces.

planCity

function planCity(layout: Required<CityOptions>, kinds: ReadonlyMap<string, ThingKind>, rng: Rng): CityPlan

Lay out a city.

validateConsumeGrow

function validateConsumeGrow(options: HoleOptions = {}): string[]

Check a round's options. Empty when it can be played.

Types

CityOptions

interface CityOptions

How a city is laid out.

  • blocks?: [number, number] — Blocks across and down. Default [6, 6].
  • block?: number — A block's side, in units. Default 20.
  • road?: number — A road's width, pavements included. Default 8.
  • parks?: number — The share of blocks that are parks. Default 0.2.
  • lots?: number — Lots to a block's side: 2 makes four lots a block. Default 2.
  • buildings?: KindMix — What stands on a lot. Default houses, shops, towers and skyscrapers.
  • park?: KindMix — What stands in a park. Default trees, benches and kiosks.
  • parkThings?: number — Things in a park. Default 8.
  • street?: KindMix — What stands on a pavement. Default cones, hydrants, bins, benches, lamps and trees.
  • props?: number — Things along each side of a block. Default 4.
  • walkers?: KindMix — What walks the pavements. Default people.
  • walking?: number — Walkers round each block. Default 3.
  • vehicles?: KindMix — What drives the roads. Default cars, vans and buses.
  • traffic?: number — Vehicles on each road. Default 2.

CityPlan

interface CityPlan

A city.

  • width: number — Width of the city in city units: the blocks and the roads between and around them.
  • height: number — Height of the city in city units.
  • roads: Rect[] — The roads, pavements included: a full-height strip beside and between the columns of blocks and a full-width one beside and between the rows.
  • blocks: (Rect & { park: boolean })[] — The blocks between the roads, row by row; park marks a park rather than a built-up block.
  • things: PlacedThing[] — Every thing in the city at the start of a round.

ConsumeGrowApi

interface ConsumeGrowApi

What the consume-grow pack adds to a game: game.consumeGrow.

  • create: typeof createConsumeGrow — Start a round.
  • validate: typeof validateConsumeGrow — Check a round's options.
  • kinds: typeof cityKinds — The built-in kinds of thing.
  • layout: typeof cityLayout — The built-in city layout.
  • autopilot: typeof createHoleAutopilot — A player for an attract mode or a test.

ConsumeGrowGame

interface ConsumeGrowGame

A round.

  • readonly state: HoleState — The live state, changed in place as the round advances; use view().state for a copy.
  • readonly city: Pick<CityPlan, 'width' | 'height' | 'roads' | 'blocks'> — The city's ground: its size, roads and blocks.
  • readonly options: Readonly<{ kinds: ThingKind[]; growth: HoleGrowth; fit: number; eat: number; speed: number }> — The kinds, growth, fit, eat ratio and base speed actually in use, after defaults are filled in.
  • move(x: number, y: number): void — The player's way to go; longer than 1 is cut to 1.
  • step(dt: number): void — Advance, in fixed steps of 1/60 s.
  • radiusAt(level: number): number — The radius at a level.
  • fits(radius: number, kind: string): boolean — Whether a hole of this radius fits a kind.
  • suggest(id: string): { x: number; y: number } — The way a rival would go now, for any hole: what the autopilot plays by.
  • view(): HoleView — What is where now: a copy of the state, the things in the city and the ranking by points.
  • drain(): HoleEvent[] — Take the events since the last drain, oldest first (at most the latest 512), and clear them.
  • snapshot(): HoleSave — Save the round as plain JSON; the city's ground is not included, as it follows from the seed and options.
  • restore(save: HoleSave): void — Return to a saved round and discard pending events; the save must come from a round with the same seed and options.

Hole

interface Hole

A hole.

  • id: string — Stable identifier: 'you' for the player, 'rival1', 'rival2' and so on for the rest.
  • name: string — Display name: the player's is 'You'; a rival's comes from HoleOptions.names, numbered once the names run out.
  • player: boolean — Whether the player steers this hole; true only for the first.
  • x: number — Centre across the city, in city units from its left edge.
  • y: number — Centre down the city, in city units from its top edge.
  • radius: number — Radius in city units, set by the level through radiusAt.
  • level: number — Growth level, from 1 up to growth.max.
  • xp: number — Points toward the next level, and how many it needs.
  • next: number — Points this level's bar holds: growth.first times growth.factor for each level above 1, rounded.
  • points: number — The round's score.
  • alive: boolean — False while the hole is swallowed: waiting to come back in classic, out for good in battle.
  • out: boolean — Battle: swallowed, and out of the round.
  • respawn: number — Seconds until it comes back, while swallowed in classic.
  • holes: number — Holes it has swallowed.
  • dx: number — The way it is going, and (for a rival) where it is heading and when it next thinks.
  • dy: number — Heading down the city (the y axis), as dx is across; at most length 1 together.
  • aim: { x: number; y: number } — A rival's wander target, in city units: where it goes when nothing is worth chasing or fleeing.
  • think: number — Seconds until a rival next chooses a heading; unused by the player's hole.

HoleAutopilot

interface HoleAutopilot

A player that picks the way to go.

  • next(): { x: number; y: number } — The way to go now.

HoleEvent

type HoleEvent = | { kind: 'swallow'; hole: string; thing: string; points: number } | { kind: 'level'; hole: string; level: number } | { kind: 'eat'; hole: string; victim: string; points: number } | { kind: 'respawn'; hole: string } | { kind: 'out'; hole: string } | { kind: 'end'; winner: string | null }

Something that happened.

HoleGrowth

interface HoleGrowth

How a hole grows.

  • radius: number — The radius at level 1. Default 1.
  • step: number — Wider by this each level. Default 0.3.
  • first: number — Points from level 1 to 2. Default 6.
  • factor: number — Each level needs this many times the points of the last. Default 1.3.
  • max: number — The top level. Default 30.

HoleOptions

interface HoleOptions

A round's options.

  • seed?: number — Default 1.
  • mode?: 'classic' | 'solo' | 'battle' — A timed round for the most points; a target to reach alone; or the last hole left. Default 'classic'.
  • duration?: number — Seconds in the round. Default 120.
  • rivals?: number — Rival holes. Default 7 (none in solo).
  • city?: CityOptions — The city's layout. Default cityLayout.
  • kinds?: ThingKind[] — The kinds of thing. Default cityKinds.
  • growth?: Partial<HoleGrowth> — How a hole grows.
  • speed?: number — Units a second at level 1. Default 7.
  • speedUp?: number — Faster by this share each level. Default 0.02.
  • fit?: number — A thing fits when its size is at most this share of the hole's radius. Default 0.95.
  • eat?: number — A hole swallows another this many times its radius or more. Default 1.2.
  • share?: number — The share of a swallowed hole's points the swallower gains. Default 0.5.
  • respawn?: number — Seconds before a swallowed hole comes back, in classic. Default 4.
  • target?: number — Solo: the share of the city's points to reach. Default 0.4.
  • skill?: number — How sharp the rivals are, 0 to 1: how often they think again. Default 0.7.
  • names?: string[] — Rivals' names. Default holeNames.

HoleSave

interface HoleSave

A round saved as plain JSON (the city's ground follows from the seed).

  • state: HoleState — The round state: clock, phase, winner and every hole.
  • things: Thing[] — Every thing still in the city, standing, moving or falling, with its position, path and fall progress.
  • rng: RngState — State of the random generator that places respawning holes and varies the rivals' thinking time.
  • carry: number — Seconds of step time not yet spent, less than one 1/60 s tick.

HoleState

interface HoleState

Where the round stands.

  • time: number — Seconds of play so far; stops when the round is over.
  • duration: number — Seconds the round lasts.
  • mode: 'classic' | 'solo' | 'battle' — 'classic' is timed for the most points, 'solo' a target to reach alone, 'battle' the last hole left.
  • phase: 'play' | 'over' — 'play' while the round runs, 'over' once it has ended.
  • winner: string | null — The winner's id once it is over, or null (solo, short of the target).
  • target: number — Solo: the points to reach.
  • holes: Hole[] — Every hole, the player's ('you') first.

HoleView

interface HoleView

What is where.

  • state: HoleState — A deep copy of the round state, safe to keep after the round moves on.
  • things: { id: number; kind: string; x: number; y: number; angle: number; size: number; height: number; fall: number; into: string | null }[] — Things standing, walking, driving and falling: fall from 0 to 1 while it drops into into.
  • ranking: { id: string; name: string; points: number; alive: boolean; out: boolean }[] — Holes in the round, by points.

KindMix

type KindMix = { kind: string; weight?: number }[]

A choice of kinds, by weight.

PlacedThing

interface PlacedThing

A thing as the city places it.

  • kind: string — The ThingKind.id of what stands here.
  • x: number — Position across the city, in city units.
  • y: number — Position down the city, in city units.
  • angle: number — Facing in radians, 0 along the x axis.
  • path: ThingPath | null — The route a walker or vehicle follows, or null for a thing that stays put.

Rect

interface Rect

A rectangle in city units, measured from the city's top-left corner.

  • x: number — Left edge.
  • y: number — Top edge.
  • w: number — Width across (the x axis).
  • h: number — Height down (the y axis).

ThingKind

interface ThingKind

A kind of thing.

  • id: string — Unique name of the kind: what KindMix entries refer to, and the kind reported in views and swallow events.
  • size: number — Its footprint's radius: a hole swallows it once the hole is wide enough (see fit).
  • height: number — How tall it stands.
  • points: number — Points for swallowing it.
  • moves?: 'walk' | 'drive' — Walkers go round their block on the pavement; drivers go up and down their road. Default it stays put.
  • speed?: number — Units a second, for walkers and drivers.

ThingPath

type ThingPath = | { kind: 'loop'; x0: number; y0: number; x1: number; y1: number; t: number; dir: 1 | -1 } | { kind: 'line'; axis: 'x' | 'y'; at: number; lane: number; t: number; dir: 1 | -1; min: number; max: number }

How a moving thing goes: round a rectangle, or up and down a line (turning at its ends, into the other lane).