Phaser AE

Bubble trap

Pro packv3.0.0-12DArcade

Bubbles as a game mechanic of their own: blow one, trap an enemy in it, let it drift on the air currents, and pop it with a whole chain of its neighbours. Special bubbles bring floods, fire and lightning, and it all runs over the platform packs in any platform game.

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

Released October 19, 2026 · updated October 20, 2026

Bubble trap: 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

bubbleTrap

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

Install the bubble-trap API (game.bubbleTrap).

Helpers

bubbleSettings

function bubbleSettings(o: Partial<BubbleSettings> = {}): BubbleSettings

Defaults: Bubble Bobble on 8 px tiles.

createBubbles

function createBubbles(o: BubblesOptions): BubbleField

Create a bubble field over a walker world and an enemy director.

createCurrents

function createCurrents(map: Pick<PlatformMap, "cols" | "rows" | "col" | "row"> & { wrapY?: boolean }, rows?: readonly string[] | null, fill: CurrentChar = "^"): CurrentGrid

Build a current grid for a map. rows gives one string per map row ('^' '>' 'v' '<' '.'); missing rows and characters fall back to fill (default '^': everything floats up, the common case).

createElements

function createElements(o: { world: WalkerWorld; enemies: EnemyDirector; settings?: Partial<ElementSettings>; canKill?: (enemyId: string, kind: string) => boolean }): Elements

Create the elemental effects. canKill filters which enemies they kill (default every live enemy).

decodeCurrents

function decodeCurrents(rle: string, cols: number, rows: number): string[]

Expand a run-length encoded current grid ("v32>14..." : a direction followed by an optional count) into rows.

elementSettings

function elementSettings(o: Partial<ElementSettings> = {}): ElementSettings

Defaults: Bubble Bobble (estimates from play).

Types

Bolt

interface Bolt

A lightning bolt.

  • id: string — Stable id.
  • x: number — Centre x.
  • y: number — Centre y.
  • dir: -1 | 1 — Direction.
  • age: number — Seconds alive.
  • by: string — Who released it.

Bubble

interface Bubble

A bubble, as read by renderers and rules.

  • id: string — Stable id ('bubble-N'). Its surface id is bubble:<id>.
  • kind: BubbleKind — What it holds.
  • x: number — Centre x.
  • y: number — Centre y.
  • vx: number — Velocity x, units per second.
  • vy: number — Velocity y, units per second.
  • r: number — Radius.
  • phase: "shot" | "rise" | "float" — 'shot' while travelling from the blower, 'rise' while floating straight up into the play area from below (special bubbles entering through a gap), then 'float' on the currents.
  • riseTo: number — Height a rising bubble climbs to before the currents take it.
  • age: number — Seconds alive.
  • life: number — Seconds until it pops by itself (empty, special) or its enemy escapes (enemy).
  • warn: number — 0 until the warning starts, rising to 1 at the pop or escape.
  • enemy: string — Trapped enemy id ('' none).
  • enemyKind: string — Trapped enemy kind ('' none).
  • letter: string — Letter carried by a letter bubble ('' none).
  • owner: string — Body that blew it ('' for spawned bubbles).
  • dir: -1 | 1 — Travel direction of a shot.
  • travelled: number — Distance travelled as a shot.
  • range: number — Distance a shot travels before floating.
  • speed: number — Shot speed, units per second.
  • snapped: boolean — Moved discontinuously this step (wrapped): renderers should snap.

BubbleEvent

type BubbleEvent = | { kind: "blown"; id: string; owner: string; x: number; y: number } | { kind: "spawned"; id: string; bubble: BubbleKind; x: number; y: number } | { kind: "captured"; id: string; enemy: string; enemyKind: string; x: number; y: number } | { kind: "escaped"; id: string; enemy: string; enemyKind: string; x: number; y: number } | { kind: "popped"; id: string; bubble: BubbleKind; by: string; x: number; y: number; enemy: string; letter: string; chain: number } | { kind: "expired"; id: string; bubble: BubbleKind; x: number; y: number } | { kind: "wall-pop"; id: string; owner: string; x: number; y: number } | { kind: "wrapped"; id: string }

Bubble events from drain().

BubbleField

interface BubbleField extends SurfaceProvider

The bubble field: a SurfaceProvider plus blowing, trapping, popping and queries.

  • readonly settings: Readonly<BubbleSettings> — Settings in force (mutable through configure).
  • readonly bubbles: Bubble[] — Copies of every bubble.
  • bubble(id: string): Bubble | null — One bubble or null.
  • count(kind?: BubbleKind): number — Live bubbles, optionally of one kind.
  • configure(o: Partial<BubbleSettings>): void — Change settings (per round: float speed, trap time ...).
  • setCurrents(currents: readonly string[] | CurrentGrid | null): void — Replace the currents.
  • readonly currents: CurrentGrid — The current grid.
  • blow(bodyId: string, o?: { range?: number; speed?: number }): string | null — Blow a bubble from a body (in front of it, in its facing direction). Returns the id, or null when full.
  • spawn(kind: BubbleKind, x: number, y: number, o?: { letter?: string; vx?: number; vy?: number; riseTo?: number }): string | null — Spawn a floating bubble (special, letter, empty). With riseTo it first rises straight up to that height, ignoring the currents (entering from below the floor). Returns the id, or null when full.
  • trap(enemyId: string, o?: { force?: boolean; life?: number }): string | null — Trap an enemy in a new floating bubble at its position (a beaten boss, a magic item). force ignores canCapture; life overrides the escape time. Returns the bubble id or null.
  • pop(id: string, by?: string): PopResult | null — Pop a bubble and every bubble touching it (a chain). Trapped enemies are removed from the director.
  • touching(bodyId: string, inset?: number): string[] — Bubbles a body touches (popping contact), excluding bubbles it has just bounced on and its own fresh shots.
  • noteBounce(bodyId: string, bubbleId: string): void — Record that a body bounced on a bubble (it is not popped by that body for a moment).
  • release(id: string): void — Release a trapped enemy now (it escapes).
  • clear(release?: boolean): void — Remove every bubble (trapped enemies are released when release is true, otherwise removed).
  • drain(): BubbleEvent[] — Events since the last drain.
  • snapshot(): BubblesSnapshot — Serialisable state.
  • restore(s: BubblesSnapshot): void — Restore a snapshot (restore the world and director too).

BubbleKind

type BubbleKind = "empty" | "enemy" | "water" | "fire" | "lightning" | "letter"

What a bubble holds.

BubbleSettings

interface BubbleSettings

Bubble rules. Distances in world units, times in seconds.

  • radius: number — Bubble radius. Default 8 (a 16 px bubble on 8 px tiles).
  • shotSpeed: number — Speed of a freshly blown bubble, units per second. Default 192.
  • shotRange: number — Distance a blown bubble travels before it starts to float. Default 40.
  • floatSpeed: number — Floating speed along the currents, units per second. Default 18.
  • emptyLife: number — Seconds an empty bubble floats before it pops by itself (the last warn seconds flash). Default 14.
  • trapTime: number — Seconds a trapped enemy stays before it starts to break out (then warn seconds of warning). Default 20.
  • warn: number — Warning seconds before a trapped enemy escapes (its bubble changes colour). Default 3.
  • emptyWarn: number — Warning seconds before an empty, special or letter bubble pops by itself (a short fast flash). Default 1.
  • specialLife: number — Seconds special and letter bubbles float before they pop by themselves. Default 24.
  • riseSpeed: number — Speed of a bubble rising in from below a floor gap (the 'rise' phase), units per second. Default 60.
  • bounce: number — Launch speed for a rider that lands on a bubble while holding jump. Default 230. 0 disables riding.
  • chain: number — Bubbles closer than this many diameters pop together. Default 1.15.
  • separation: number — Speed at which overlapping floating bubbles push apart, units per second. Default 36.
  • max: number — Most bubbles at once (resource bound; blowing more fails). Default 64.
  • wallPop: boolean — A blown bubble that hits a wall straight away pops at once (Bubble Bobble). Default true.
  • wrap: boolean — Bubbles that float out of a gap in the top or bottom re-enter at the other side when the map wraps. Default true.
  • riderTag: string — Only bodies with this tag ride bubbles. Default 'player' ('' for everyone).
  • captureWhileFloating: boolean — Floating bubbles trap enemies too (not in Bubble Bobble: only a blown bubble traps). Default false.

BubblesOptions

interface BubblesOptions

Options for createBubbles.

  • world: WalkerWorld — The walker world (bodies are trapped, frozen and released through it).
  • enemies: EnemyDirector — The enemy director (trapped enemies are suspended, resumed and removed through it).
  • settings?: Partial<BubbleSettings> — Rules (partial; defaults are Bubble Bobble's).
  • currents?: readonly string[] | CurrentGrid | null — Air currents as strings over the map ('^' '>' 'v' '<' '.'); default everything up.
  • canCapture?: (enemyId: string, kind: string) => boolean — Which enemies a bubble may trap (default: any active enemy).
  • id?: string — Provider id. Default 'bubbles'.
  • attach?: boolean — Add itself to the world as a surface provider. Default true.

BubblesSnapshot

interface BubblesSnapshot

Serialisable state.

  • bubbles: Bubble[] — Bubbles.
  • seq: number — Id counter.
  • chains: number — Chain counter.
  • settings: BubbleSettings — Settings in force.
  • bounces: [string, string, number][] — Recent bounces (body id, bubble id, seconds left).
  • events: BubbleEvent[] — Undrained events.

CurrentChar

type CurrentChar = "^" | ">" | "v" | "<" | "."

One current direction: up, right, down, left or still.

CurrentGrid

interface CurrentGrid

A direction field laid over a platform map's cells.

  • readonly cols: number — Columns (the map's).
  • readonly rows: number — Rows (the map's).
  • at(col: number, row: number): CurrentChar — The direction character of a cell (columns wrap; rows wrap when the map wraps vertically, otherwise outside rows are '^').
  • dirAt(x: number, y: number): [number, number] — Unit direction at a world point: [dx, dy] with each in {-1, 0, 1}.
  • toRows(): string[] — The grid as strings (for renderers and debugging).

ElementEvent

type ElementEvent = | { kind: "element-kill"; element: "water" | "fire" | "lightning"; enemy: string; enemyKind: string; x: number; y: number; by: string } | { kind: "ride-start"; player: string; flow: string } | { kind: "ride-end"; player: string; x: number; feet: number; escaped: boolean } | { kind: "burned"; player: string; fire: string } | { kind: "flow-end"; flow: string }

Element events.

Elements

interface Elements

Elemental effects over a walker world.

  • readonly settings: Readonly<ElementSettings> — Settings.
  • configure(o: Partial<ElementSettings>): void — Change settings (applies at once).
  • readonly flows: WaterFlow[] — Copies of the flows.
  • readonly fires: FireDrop[] — Copies of the fires.
  • readonly bolts: Bolt[] — Copies of the bolts.
  • water(x: number, y: number, dir: -1 | 1, by?: string): string — Start a water flow at a point, running in dir once it reaches a floor.
  • fire(x: number, y: number, by?: string): string — Drop a fireball from a point.
  • lightning(x: number, y: number, dir: -1 | 1, by?: string): string — Release a lightning bolt from a point.
  • readonly active: boolean — Whether any effect is running.
  • step(dt: number): void — Advance one step (call after the walker world steps).
  • owner(id: string): string — Who released an effect ('' unknown).
  • release(bodyId: string): boolean — Set a carried player down now (it died, or the rules need it free). Returns whether it was riding.
  • clear(): void — Stop everything (riders are released).
  • drain(): ElementEvent[] — Events since the last drain.
  • snapshot(): ElementsSnapshot — Serialisable state.
  • restore(s: ElementsSnapshot): void — Restore.

ElementSettings

interface ElementSettings

Element rules. Speeds in units per second, times in seconds.

  • waterSpeed: number — Water flow speed. Default 110.
  • waterLength: number — Trail points kept behind the head (spacing waterStep). Default 24.
  • waterStep: number — Spacing of trail points. Default 4.
  • waterLife: number — Optional bound: seconds a flow runs before it dries up (0: no limit). Default 0: a flow ends only by draining out of the bottom of the map, as Bubble Bobble's water does. For games that want a shorter or bounded flood.
  • waterTurns: number — Optional bound: wall turns before the flow drains away (0: no limit). Default 0.
  • fireFall: number — Fireball drop speed. Default 160.
  • fireSpread: number — Cells the fire spreads either side along the floor it lands on. Default 3.
  • fireBurn: number — Seconds the fire burns. Default 4.
  • fireStun: number — Seconds a player who touches the fire is stunned (0: no effect). Default 1.2.
  • boltSpeed: number — Lightning bolt speed. Default 260.
  • boltSize: number — Lightning bolt half size. Default 7.
  • playerTag: string — Player tag (riders and the stunned). Default 'player'.
  • waterEscape: "none" | "jump" — What a player caught in a water flow can do: 'none' (carried until the flow drains, as the sources describe Bubble Bobble's water) or 'jump' (a fresh jump press hops out of the stream: a house rule). Default 'none'.
  • waterHop: number — Upward speed of a hop out of the water ('jump'), units per second. Default 220.

ElementsSnapshot

interface ElementsSnapshot

Serialisable state.

  • flows: WaterFlow[] — Flows.
  • fires: FireDrop[] — Fires.
  • bolts: Bolt[] — Bolts.
  • seq: number — Id counter.
  • owners: [string, string][] — Who released each element.
  • immune: [string, number][] — Players briefly immune to being caught again after hopping out.
  • settings: ElementSettings — Settings in force.
  • events: ElementEvent[] — Undrained events.

FireDrop

interface FireDrop

A dropped fireball and its burning patch.

  • id: string — Stable id.
  • x: number — Drop x.
  • y: number — Drop y (centre while falling, floor line once landed).
  • landed: boolean — Landed and burning.
  • cells: number[] — Burning cells: centre x of each cell on the floor line y.
  • age: number — Seconds burning.
  • burned: string[] — Players already stunned by it.

PoppedEnemy

interface PoppedEnemy

A popped enemy, for the rules (score, fruit).

  • id: string — Enemy id (already removed from the director).
  • kind: string — Enemy kind.
  • x: number — Where its bubble was.
  • y: number — Where its bubble was.

PopResult

interface PopResult

The result of one pop (a chain).

  • bubbles: Bubble[] — Every bubble popped together (copies, before removal).
  • enemies: PoppedEnemy[] — Trapped enemies in the chain, in pop order.
  • by: string — Who popped it ('' for none: expiry, effects).

WaterFlow

interface WaterFlow

A water flow.

  • id: string — Stable id.
  • x: number — Head x.
  • feet: number — Head feet (the floor line it runs on).
  • dir: -1 | 1 — Direction along floors.
  • falling: boolean — Falling down a gap.
  • trail: [number, number][] — Trail points [x, feet], oldest first.
  • age: number — Seconds alive.
  • turns: number — Wall turns so far.
  • ended: boolean — The head has stopped (drained or dried up); the trail drains away.
  • riders: string[] — Player bodies riding it.
  • held: string[] — Riders still holding the jump they held when caught (a new press is needed to hop out).