Phaser AE

Flight formation shooter

Pro packv3.0.0-12DArcade

A fixed shooter where enemies fly in, form up and dive: flight programs, a swaying formation, dives with escorts, tractor-beam capture and rescue, and challenging stages. Everything is data, and a classic preset is the starting point rather than the only 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

Flight formation shooter: 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

flightFormation

function flightFormation(): Pack<{ flightFormation: FlightFormationApi }>

Install the renderer-independent flight-formation rules (Galaga).

Helpers

createGalaga

function createGalaga(config: GalagaConfig)

Create an independent flight-formation game. No renderer or kernel clock is required.

galagaEntry

function galagaEntry(slots: GalagaSlot[], size = 8, waveGap = 3): EntryWave[]

Entry groups for any slot list: groups of size in slot order, cycling the five arcade approaches.

galagaPreset

function galagaPreset(o: GalagaPresetOptions = {}): GalagaConfig

Galaga (Namco, 1981) as data. Classic values: 224x288 screen; 4 Boss Galaga, 16 butterflies (Goei) and 20 bees (Zako) entering in five groups of eight; sway while arriving, then breathing; bees 50/100, butterflies 80/160, bosses 150 in formation and 400/800/1600 diving with 0/1/2 escorts already shot, a captured fighter 500/1000; bosses take two hits; tractor-beam capture and rescue into a dual fighter; challenging stages 3, 7, 11... with 40 one-hit flyers, 100 per hit, 10,000 for all 40 and group bonuses of 1,000-3,000; two missiles in flight; extra fighters at 20,000, 70,000 and every 70,000. Flight programs are reference-style approximations, not ROM tables.

galagaPrograms

function galagaPrograms(): Record<string, FlightProgram>

Flight programs of the classic preset. Dives are authored for the RIGHT half; the left half mirrors them.

galagaSlots

function galagaSlots(rows: GalagaRow[])

Slots for centred rows on a grid as wide as the widest row.

ramp

function ramp(r: Ramp, stage: number)

Evaluate a ramp at a 1-based stage.

Classes

Galaga

class Galaga

Owns a flight-formation game: entries, formation sway/breathing, dives, escorts, capture, rescue, challenging stages.

  • readonly config: GalagaConfig — The validated definition (a private copy).
  • constructor(config: GalagaConfig)
  • isChallenging(stage: number) — Is this 1-based stage a challenging stage?
  • get state(): GalagaState — A copy of the whole state.
  • slotPosition(slotId: string, offsetY = 0) — Where a slot sits right now, including sway and breathing.
  • get beams() — Active beams for drawing.
  • tick(seconds: number, input: GalagaInput = {}) — Advance by real seconds in fixed steps.
  • step(input: GalagaInput = {}) — Advance exactly one fixed step.
  • launchDive(id?: string): string | null — Launch a dive now by the configured selection rules; returns the leader id or null.
  • drain() — Take the events since the last drain.
  • snapshot() — Serializable snapshot of the whole game.
  • restore(snap: ReturnType<Galaga["snapshot"]>) — Restore a snapshot taken from the same configuration.
  • reset(stage = 1) — New game from stage (default 1).

Types

EntryWave

interface EntryWave

One entry group: members leave from start along program, gap seconds apart (or in pairs).

  • members: string[] — Slot ids (normal stages) or kind ids (challenging stages).
  • program: string — Flight program the members fly.
  • start: { x: number; y: number } — Where the first member appears.
  • heading: number — Initial heading in degrees (0 right, 90 down).
  • mirror?: boolean — Mirror the program (negated turns, heading reflected).
  • pairs?: "mirror" | "abreast" — Members fly two abreast, the second mirrored about the screen centre (top entries) or offset.
  • gap: number — Seconds between members (or pairs).
  • at: number — Seconds after the stage's entry starts.

FlightEnd

type FlightEnd = "formation" | "wrap" | "exit" | "beam"

How a program ends.

FlightFormationApi

type FlightFormationApi = typeof galaga & typeof presets

game.flightFormation: the flight-formation rules owner, its preset and its builders.

FlightProgram

interface FlightProgram

A named program and its ending.

  • steps: FlightStep[] — Steps flown in order.
  • then: FlightEnd — What the flyer does when the steps run out.

FlightStep

interface FlightStep

One step of a flight program.

  • seconds: number — Duration of the step.
  • turn?: number — Degrees per second, positive = clockwise on screen. Negated when the program is mirrored.
  • speed?: number — Multiplies the flight's base speed. Default 1.
  • aim?: number — Steer toward the player at up to this many degrees per second.
  • untilY?: number — End the step early once the flyer is at or below this y.
  • fire?: boolean — Shots may be fired during this step (up to the dive's perDive budget, gap apart, within the fire band).

GalagaConfig

interface GalagaConfig

Complete flight-formation definition; galagaPreset builds the arcade one.

  • width: number — Playfield width in model pixels.
  • height: number — Playfield height in model pixels.
  • hz: number — Fixed simulation steps per second.
  • seed?: number — Seed for selection, capture and entry-fire chances.
  • kinds: GalagaKind[] — Enemy kinds.
  • formation: { slots: GalagaSlot[]; /** Grid width in columns (slot cols index into it). */ columns: number; centerX: number; /** Centre y of row 0. */ top: number; spacingX: number; spacingY: number; /** Side-to-side drift while the fleet is still arriving. */ sway: { amplitude: number; period: number }; /** Expansion and contraction once every enemy has arrived (fraction of spacing). */ breathe: { amount: number; period: number }; } — Formation grid, slots and motion.
  • programs: Record<string, FlightProgram> — Named flight programs for entries, dives, captures and challenging stages.
  • stages: { /** First challenging stage (null = none) and the interval after it. Original: 3 and 4. */ challengingFirst: number | null; challengingEvery: number; /** Entry waves for normal stages. */ entry: EntryWave[]; /** Challenging stage wave sets, cycled by challenging-stage index. */ challenging: EntryWave[][]; introSeconds: number; clearSeconds: number; resultSeconds: number; } — Stage sequence: entries, challenging stages and their timings.
  • entrySpeed: Ramp — Entry flight speed by stage (px/s).
  • attack: { speed: Ramp; /** Seconds between dive launches. */ interval: Ramp; /** Simultaneous dive groups. */ maxDivers: Ramp; /** Relative chance a dive picks each kind. */ weights: Record<string, number>; /** Once this few remain the attack quickens: half the interval and one more simultaneous dive. */ frenzyAtOrBelow: number; /** Dives start only after the whole fleet has arrived. */ afterEntry: boolean; /** Speed while flying home to a slot. */ homeSpeed: number; } — Dive selection, timing and speed rules.
  • capture: { program: string; /** Chance a capture-capable kind's dive is a capture dive when a capture is allowed. */ chance: Ramp; beamWidth: number; /** Beam reach below the boss. */ beamLength: number; /** Opening, holding and closing seconds. */ beamOpen: number; beamHold: number; beamClose: number; pullSeconds: number; /** The captured ship's enemy kind (scored when shot). */ fighterKind: string; /** Program the hostile fighter dives with after its boss died in formation. */ hostileDive: string; } | null — Tractor-beam capture rules, or null for none.
  • fire: { speed: Ramp; maxActive: number; /** Shots per dive. */ perDive: Ramp; /** Seconds between a diver's shots. */ gap: number; /** Only fire between these heights. */ minY: number; maxY: number; /** Largest angle from straight down. */ maxAngle: number; /** Chance an entering enemy fires once. */ entryChance: Ramp; width: number; height: number; } — Enemy fire rules.
  • player: { y: number; width: number; height: number; speed: number; minX: number; maxX: number; lives: number; extraLives: number[]; /** After the last listed threshold, another every this many points (0 = none). */ extraEvery: number; /** Missiles in flight for a single fighter; a dual fighter fires pairs up to twice this. Original: 2. */ maxShots: number; shotSpeed: number; shotWidth: number; shotHeight: number; dualSpacing: number; requireRelease: boolean; deathSeconds: number; } — Fighter movement, missiles, lives and extra lives.
  • challengeScoring: { perHit: number; perfect: number; /** Bonus for destroying a whole group, by challenging-stage index (last repeats). */ groupBonus: number[]; } — Challenging-stage bonuses.

GalagaEnemy

interface GalagaEnemy

One enemy (or captured fighter) with its flight state.

  • id: string — Stable id (slot id in normal stages).
  • kind: string — Kind id.
  • slot: string | null — Formation slot, or null for challenging flyers.
  • slotOffsetY: number — Extra formation offset (a hostile fighter sits above its old boss).
  • hp: number — Remaining hits.
  • phase: GalagaPhase — Current phase.
  • x: number — Centre x.
  • y: number — Centre y.
  • heading: number — Heading in degrees (0 right, 90 down).
  • program: string — Flight program being flown.
  • step: number — Index of the current program step.
  • stepTime: number — Seconds into the current step.
  • mirror: boolean — Flying the mirrored program.
  • entering: boolean — Flying an entry route (scores diving points, cannot be selected to attack).
  • diving: boolean — Part of a dive: scores its diving value.
  • spawnAt: number — Stage time at which it enters.
  • wave: number — Entry group index (-1 for a captured fighter).
  • leader: string | null — Boss this escort follows, if any.
  • offsetX: number — Escort offset from its leader.
  • offsetY: number — Escort offset from its leader.
  • escortsLaunched: number — Escorts that left with this boss.
  • escortsKilled: number — Of those, destroyed before this boss.
  • captive: string | null — Id of a captured fighter riding with this enemy.
  • rider: string | null — Rider/puller: the boss this fighter belongs to.
  • beamTime: number — Seconds into the tractor beam.
  • fireLeft: number — Shots still to fire on this flight.
  • fireClock: number — Seconds until the next shot may fire.
  • animTime: number — Seconds of the current pull/rescue animation.
  • exit: boolean — While leaving: true = gone for good off screen, false = wrap back in from the top.
  • speed: number — Base flight speed for this flight (px/s).

GalagaEvent

type GalagaEvent = | { kind: "stage-start"; stage: number; challenging: boolean } | { kind: "player-fire"; x: number; y: number } | { kind: "enemy-hit"; id: string; enemy: string; x: number; y: number } | { kind: "enemy-killed"; id: string; enemy: string; score: number; x: number; y: number; diving: boolean } | { kind: "dive"; id: string; enemy: string; escorts: string[]; capture: boolean } | { kind: "beam"; id: string } | { kind: "captured"; boss: string; fighter: string } | { kind: "captive-hostile"; fighter: string } | { kind: "rescued"; fighter: string } | { kind: "player-hit"; x: number; y: number; dual: boolean } | { kind: "player-ready" } | { kind: "extra-life"; lives: number } | { kind: "group-bonus"; score: number; x: number; y: number } | { kind: "stage-clear"; stage: number } | { kind: "challenge-result"; hits: number; total: number; bonus: number; perfect: boolean } | { kind: "game-over" }

Ordered notifications for presentation: drain() them each frame.

GalagaInput

interface GalagaInput

One step of player input.

  • move?: number — -1 left, 0 still, +1 right.
  • fire?: boolean — Fire held.

GalagaKind

interface GalagaKind

An enemy type.

  • id: string — Kind id referenced by slots, waves and weights.
  • formationScore: number — Score when destroyed in formation, and while flying (entering, diving or returning).
  • divingScore: number — Score when destroyed while flying.
  • escortScores?: number[] — Diving score by number of its escorts destroyed earlier in the same dive (boss: 400/800/1600).
  • hp: number — Hits to destroy (Boss Galaga: 2).
  • width: number — Hit-box width.
  • height: number — Hit-box height.
  • dives: string[] — Dive program names; one is chosen per dive (seeded).
  • loopDives?: string[] — Dive programs used from loopFromStage on (bees start looping).
  • loopFromStage?: number — First stage that may use loopDives.
  • escorts?: { kind: string; max: number } — Escort kind and count this kind takes on a dive (boss: two butterflies).
  • captures?: boolean — This kind may carry out capture dives.

GalagaPhase

type GalagaPhase = | "waiting" | "flying" | "leaving" | "homing" | "formation" | "beaming" | "pulled" | "riding" | "rescuing" | "dead" | "gone"

Where an enemy is in its life: waiting to enter, flying a program, leaving the screen, flying home, in formation, beaming, a captured ship being pulled up, riding its boss, a rescued ship descending, dead or gone.

GalagaPresetOptions

interface GalagaPresetOptions

Options for galagaPreset.

  • rows?: GalagaRow[] — Custom rows, top first (default: 4 bosses, 2x8 butterflies, 2x10 bees).
  • groupSize?: number — Members per entry group for custom rows (default 8).
  • lives?: number — Starting fighters, including the one in play (3 = classic).
  • capture?: boolean — Boss tractor beams and captured-fighter rescue.
  • challengingFirst?: number | null — First challenging stage (null for none) and the interval after it.
  • challengingEvery?: number — Stages between challenging stages after the first (4 = classic).
  • aggression?: number — Multiplies dive frequency and simultaneous divers (1 = classic).
  • maxShots?: number — Simultaneous single-fighter missiles (2 = classic).
  • extraLives?: number[] — Scores that award an extra fighter (classic 20,000 and 70,000).
  • extraEvery?: number — After the listed scores, another fighter every this many points (classic 70,000; 0 = none).
  • seed?: number — Seed for dive selection and capture chance.

GalagaRow

interface GalagaRow

A formation row: count enemies of kind, centred on the grid.

  • kind: string — Enemy kind id for every ship in the row.
  • count: number — Ships in the row.

GalagaShot

interface GalagaShot

A missile or enemy bullet.

  • id: number — Stable id for drawing.
  • x: number — Centre x.
  • y: number — Top y for missiles, centre y for bullets.
  • vx: number — Velocity x (px/s).
  • vy: number — Velocity y (px/s).

GalagaSlot

interface GalagaSlot

A formation slot on the grid.

  • id: string — Unique slot id; also the enemy's id in normal stages.
  • kind: string — Kind that fills the slot.
  • col: number — Grid column (fractions allowed for centred rows).
  • row: number — Grid row, 0 at the top.

GalagaState

interface GalagaState

Serializable game state; read it through state.

  • time: number — Seconds since the game began.
  • stage: number — Stage number, from 1.
  • challenging: boolean — This stage is a challenging stage.
  • challengeIndex: number — Challenging stages begun so far, minus one.
  • status: GalagaStatus — Game status.
  • statusTime: number — Seconds in the current status.
  • stageTime: number — Seconds of play in this stage.
  • score: number — Score.
  • lives: number — Fighters left, including the one in play.
  • extraAwarded: number — Extra fighters awarded so far.
  • player: { x: number; alive: boolean; dual: boolean; deadTime: number; captured: boolean; pendingDual: boolean; } — The fighter: x, alive, dual, death timer, captured and a rescue waiting for respawn.
  • enemies: GalagaEnemy[] — Every enemy of the stage, and any captured fighter.
  • playerShots: GalagaShot[] — Missiles in flight.
  • enemyShots: GalagaShot[] — Enemy bullets in flight.
  • fireHeld: boolean — Fire was held on the last step (release rule).
  • entryDone: boolean — Every entering enemy has arrived or died.
  • breatheTime: number — Seconds of breathing.
  • attackClock: number — Seconds since the last dive launch.
  • side: number — Side of the next dive (-1 left, 1 right).
  • hits: number — Challenging-stage hits.
  • total: number — Enemies in this stage.
  • groupHits: number[] — Hits per entry group.
  • groupSizes: number[] — Members per entry group.
  • lastBonus: number — Last challenging-stage bonus.
  • perfect: boolean — The last challenging stage was perfect.
  • nextId: number — Next shot/captive id.
  • random: number[] — The kernel generator's state (four words from createRng), advanced in place, so a snapshot carries the stream.

GalagaStatus

type GalagaStatus = | "intro" | "playing" | "clear" | "result" | "game-over"

Stage intro banner, play, cleared, challenging-stage result, or game over.

Ramp

interface Ramp

A value that changes with the stage: base + perStage * (stage - 1), clamped to min/max.

  • base: number — Value at stage 1.
  • perStage?: number — Change per stage after the first.
  • min?: number — Lower clamp.
  • max?: number — Upper clamp.