Phaser AE

Grab-throw columns

Pro packv3.0.0-32DGrid

The grab-and-throw column puzzle: columns of colour hang from the ceiling and press down a row at a time, and the player below grabs runs of one colour and throws them back up to make vertical lines that clear and chain. The pack owns the rules, the timings, the special pieces, the objectives, the scoring and the versus hooks.

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

Grab-throw columns: 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

grabThrowColumns

function grabThrowColumns(): Pack<{ grabThrowColumns: GrabThrowColumnsApi }>

Install the grab-throw columns API (pure rules, so it needs no backend and runs headless).

Helpers

attackLevel

function attackLevel(cleared: number, chain: number): number

Magical Drop III's attack level (fandom "Gameplay overview/Magical Drop III", after maruta): ⌊(cleared − 3) / 4⌋ + 4 × (chain − 1), never below zero.

attackLines

function attackLines(level: number, table: readonly number[] = MD3_ATTACK_LINES): number

Lines for an attack level from a table (index = level, last entry repeats; an empty table sends nothing).

boardFromColumns

function boardFromColumns(columns: string[], width = columns.length): string[]

Turn columns (top to bottom, one string each) into board rows (top to bottom).

chainBonus

function chainBonus(n: number, scoring: DropScoring = MD3_SCORING): number

The chain bonus for a chain of n links. A single clear (n = 1) earns nothing extra; n = 2..doubleUntil earns chainBase × 2^(n − 2); beyond that the bonus is linearStep × (n − doubleUntil).

chainScore

function chainScore(cleared: number, chain: number, scoring: DropScoring = MD3_SCORING): number

The score of a whole chain: pieces × perPiece plus the chain bonus.

createDropGame

function createDropGame(config: DropConfig): DropGame

Create a game from a configuration (see the presets).

DROP_DIFFICULTY

const DROP_DIFFICULTY: Readonly<Record<DropDifficultyName, DropDifficulty>>

Easy / Normal / Arcade.

DROP_KINDS

const DROP_KINDS: readonly DropKind[]

Every piece-kind name, for validation.

DROP_LEGEND

const DROP_LEGEND: Readonly<Record<string, DropSpec | null>>

The default legend. Colours: r red 0, b blue 1, y yellow 2, g green 3, p purple 4, c cyan 5; ? a random colour. Upper case is the special (suit) piece of that colour, $ a random special. * rainbow, # ice, h j k l m n colour ice (red, blue, yellow, green, purple, cyan), o bubble, ^ v < > arrows, 1–9 bombs. . and space are empty.

DROP_PUZZLES

const DROP_PUZZLES: readonly DropPuzzle[]

The Drop Puzzle stages: 8 × 10 boards in five colours, after the first game's puzzle mode (clear every drop within a set number of moves). Each teaches one rule of the pack; the last combines them.

DROP_REPEAT

const DROP_REPEAT: DropRepeat

Default auto-repeat. The series moves the clown one column per joystick input with no speed limit of its own ("moving is instantaneous", PureDragon); holding the stick repeats. The repeat timing is not published, so these values are an estimate tuned to feel like the NEOGEO games: a short delay, then 20 columns per second.

DROP_STEP

const DROP_STEP: number

The fixed simulation step, seconds (120 Hz).

dropDifficulty

function dropDifficulty(name: DropDifficultyName = "normal"): DropDifficulty

Look up a difficulty row.

dropPreset

function dropPreset(name: "magical-drop" | "drop-puzzle", options: MagicalDropOptions & DropPuzzleOptions = {}): DropConfig

Preset by name.

dropPuzzlePreset

function dropPuzzlePreset(options: DropPuzzleOptions = {}): DropConfig

Drop Puzzle: a materially different configuration through the same API. An 8 × 10 board, five colours, no natural rows, authored stages cleared within a throw limit, a clown that wraps, and the series' other pieces (arrows, bombs, bubbles, ice, rainbows) placed by hand.

magicalDropLevels

function magicalDropLevels(rules: DropRuleSet = "md3", pushScale = 1): DropLevel[]

The survival level table, 20 rows. The series never published its push intervals, so the speeds are an estimate: 6 s per row at level 1, 10% faster per level, never below 1.2 s (before the difficulty scale). The mix follows Magical Drop II's survival mode as described: specials and rainbows early, then ice and bubbles join while specials and rainbows thin out. MD1 had neither rainbows nor ice; MD3 adds colour ice. About 2% of cells are specials or rainbows at first; that density is also an estimate.

magicalDropPreset

function magicalDropPreset(options: MagicalDropOptions = {}): DropConfig

Magical Drop as a configuration. md3 (the default) is the best-documented rule set: 7 × 12, specials and normals match and grab together, rainbow, ice and colour ice, bubbles, the C-button push, maruta's timings and scoring. md2 keeps specials apart in the hand; md1 also matches them only with each other and uses the first game's taller 7 × 14 field.

MD3_ATTACK_LINES

const MD3_ATTACK_LINES: readonly number[]

Lines sent per attack level, index = level (the last entry repeats). Fitted to maruta's published ranges for the "medium" group of characters (2-chain 3–5 lines, 3-chain 4–5, 4-chain 5–6, 5-chain 6–7, 6-chain 7–8, 7+ chains 8, a single clear 0). The arcade's exact three tables were published only as images.

MD3_SCORING

const MD3_SCORING: DropScoring

Magical Drop III's scoring (maruta, "010 スコアの算出").

MD3_TIMING

const MD3_TIMING: DropTiming

Magical Drop III timing. pop 0.667 s and the rise times are maruta's measurements; throw and grab speeds are read off 20 fps footage (about 9 rows in 0.12 s); pushSlide, garbageLine, ready and clear are presentation estimates.

parseBoard

function parseBoard(c: DropConfig, rows: string[]): DropSpec[][]

Parse an authored board (rows top to bottom) into columns of specs; columns must hang without gaps.

validateDropConfig

function validateDropConfig(c: DropConfig): void

Check a configuration and report the first unsupported setting clearly.

Classes

DropGame

class DropGame

One Magical Drop–style game. Inputs (move, grab, throw, pushRow, receiveGarbage) act immediately; tick(dt) advances the fixed 120 Hz simulation. Read state for drawing and drain() for events.

  • readonly config: DropConfig — The configuration in force (as given).
  • constructor(config: DropConfig) — Build a game; throws a clear error for unsupported settings.
  • reset(): void — Start again from the configured start level (same seed sequence continues; use a new game to repeat a seed).
  • goToLevel(level: number): void — Jump to a level or stage (1-based) on a fresh board.
  • retry(): void — Replay the current level or stage on a fresh board (a puzzle retry).
  • levelRow(level = this.level): DropLevel — The level row in force (the last row repeats).
  • stage(level = this.level) — The puzzle stage in force for the clear objective, or null.
  • get busy(): boolean — True while a clear, rise or wipe is in progress: natural rows and garbage wait, and further clears chain.
  • get state(): DropGameState — A copy of the game for presentation.
  • pieceAt(col: number, row: number): DropPieceState | null — The piece at a column and row (0 = top), or null.
  • drain(): DropEvent[] — Take the events since the last drain.
  • move(dir: -1 | 1): boolean — Move the clown one column left (−1) or right (+1); wraps when clown.wrap is set.
  • moveTo(col: number): boolean — Put the clown under a column directly (a touch tap). Movement in the series is instantaneous.
  • grab(): boolean — Grab the bottom piece in the clown's column and every compatible piece directly above it.
  • throw(): boolean — Throw every held piece up under the clown's column.
  • pushRow(): boolean — Push a row in now (MD3's C button), when rules.manualPush allows.
  • receiveGarbage(lines: GarbageLine[] | number): number — Queue incoming garbage (versus play). Lines enter from the ceiling one at a time, only while this board is idle (a running chain holds them back, as in the series), up to garbage.cap waiting. A number queues that many full lines of random colours.
  • offsetGarbage(count: number): number — Cancel up to count waiting garbage lines, newest first (an offsetting rule for a versus layer). Returns how many.
  • tick(dt: number): DropGameState — Advance by dt seconds (fixed 120 Hz steps inside). Returns the new state.
  • settle(maxSeconds = 30): DropGameState — Run fixed steps until the board is idle (no clears, risings, wipes or flights) or maxSeconds pass.
  • snapshot(): string — A deep, JSON-safe copy of the whole game.
  • restore(snapshot: string): void — Restore a snapshot() taken from a game with the same configuration.

DropRepeater

class DropRepeater

Turns held left/right state into clown steps: one step on the press, then repeats after delay every rate. The most recently pressed direction wins when both are held.

  • readonly repeat: DropRepeat — The timing in force.
  • constructor(repeat: DropRepeat = DROP_REPEAT) — Build with a timing (default DROP_REPEAT).
  • update(dt: number, left: boolean, right: boolean, pressedLeft = false, pressedRight = false): number — Advance by dt with the current held state; returns signed steps to take this frame (usually −1, 0 or 1). pressedLeft/pressedRight mark a fresh press this frame (so a tap shorter than a frame still steps).
  • reset(): void — Forget the held state.

Types

ArrowDir

type ArrowDir = "up" | "down" | "left" | "right"

An arrow piece's direction (on screen: up is toward the ceiling).

DropChance

interface DropChance

A chance for a generated cell to be a given piece (instead of a plain colour).

  • spec: DropSpec — The piece. A colour of −1 (or absent) on a coloured kind means "a random colour".
  • chance: number — Probability per generated cell, 0..1.

DropConfig

interface DropConfig

The board's dimensions, colours and contents.

  • cols: number — Columns (MD1–MD3 arcade: 7; Super Famicom: 6).
  • rows: number — Rows of pieces above the clown's line (MD3: 12). A column longer than this loses.
  • colours: number — Normal colours in play (the series: 4).
  • rules: DropRules — Rule variations.
  • timing: DropTiming — Timing.
  • legend: Record<string, DropSpec | null> — Character → piece, for boards, strips and stages.
  • startRows: number — Rows generated at the start of each level (ignored when board or a stage gives the board).
  • board: string[] | null — An authored starting board (rows top to bottom), used for every level when set.
  • generator: DropGenerator — Natural row source.
  • levels: { table: DropLevel[]; start: number; every: number } — Level table (the last row repeats), start level and pieces per level-up in endless play (0 = never).
  • objective: DropObjective — The objective.
  • stages: DropStage[] — Puzzle stages for the clear objective (level n plays stage n).
  • limits: { throws: number; seconds: number; fastPush: number } — Default limits (stages may override): throws (0 = none), seconds (0 = none) and the push interval once time is up.
  • clown: { start: number; wrap: boolean } — Clown start column and whether moving off one edge wraps to the other (MD2 survival).
  • scoring: DropScoring — Scoring.
  • attack: { table: number[]; minChain: number } — Attack output for versus play: lines per attack level and the shortest chain that attacks (the series: 2).
  • garbage: { cap: number } — Most garbage lines that can wait in the incoming queue (MD3: 8).
  • seed: number — Seed for rows, specials and garbage colours.

DropDifficulty

interface DropDifficulty

Difficulty scaling applied by the presets. Arcade is the series' (or the best estimate of it).

  • pushScale: number — Multiplier on every push interval (higher is slower).
  • levelEvery: number — Pieces cleared per level-up in endless play.
  • quota: number — Quota per level in quota mode.
  • extraThrows: number — Extra throws over par on puzzle stages.
  • puzzleSeconds: number — Puzzle stage time limit in seconds (0 = none).

DropDifficultyName

type DropDifficultyName = "easy" | "normal" | "arcade"

Difficulty names shared by every preset.

DropEvent

type DropEvent = | { kind: "move"; col: number; wrapped: boolean } | { kind: "grab"; col: number; ids: number[]; rows: number[] } | { kind: "grab-refused"; col: number; reason: "empty" | "colour" | "blocked" | "full" | "ungrabbable" } | { kind: "throw"; col: number; ids: number[] } | { kind: "land"; col: number; ids: number[] } | { kind: "clear"; ids: number[]; colour: number; chain: number; count: number; cause: "match" | "wipe" | "arrow" | "bomb" } | { kind: "bubble-pop"; ids: number[] } | { kind: "thaw"; ids: number[]; colour: number; into: DropKind } | { kind: "wipe"; colour: number; chain: number } | { kind: "vanish"; ids: number[] } | { kind: "chain"; chain: number } | { kind: "chain-end"; chain: number; cleared: number; score: number; attack: number } | { kind: "attack"; chain: number; cleared: number; level: number; lines: number } | { kind: "bomb-tick"; id: number; count: number } | { kind: "push"; manual: boolean; ids: number[] } | { kind: "garbage"; ids: number[]; cols: number[] } | { kind: "garbage-queued"; lines: number; pending: number } | { kind: "score"; points: number; total: number } | { kind: "level-up"; level: number } | { kind: "level-start"; level: number } | { kind: "level-clear"; level: number } | { kind: "lost"; cause: DropLoss } | { kind: "complete"; score: number }

Events, in order, drained with drain().

DropGameState

interface DropGameState

A copy of the whole game for presentation.

  • status: DropStatus — Status.
  • statusTime: number — Seconds in this status.
  • statusRemaining: number — Seconds left in a timed status (ready, clear), else 0.
  • cols: number — Columns.
  • rows: number — Rows.
  • level: number — Level (1-based); for puzzle stages, the stage number.
  • stage: string — The stage's name for the clear objective, else "".
  • score: number — Score.
  • cleared: number — Pieces cleared this game (bubbles excluded).
  • levelCleared: number — Pieces cleared this level.
  • quotaLeft: number — Pieces still needed (quota objective), else −1.
  • throwsLeft: number — Throws left under a throw limit, else −1.
  • timeLeft: number — Seconds left under a time limit or the time objective, else −1.
  • chain: number — The current chain link while a chain runs, else 0.
  • maxChain: number — Longest chain this game.
  • busy: boolean — Clears, risings or wipes in progress (natural pushes and garbage wait).
  • clown: { col: number; jump: number } — The clown's column and a counter that bumps on wrap-around jumps (snap the sprite).
  • hand: DropHeldState[] — Held pieces, the one nearest the ceiling first.
  • pieces: DropPieceState[] — Pieces on the board.
  • heights: number[] — Column lengths (clocks included).
  • pendingGarbage: number — Garbage lines waiting.
  • pushIn: number — Seconds to the next natural push (−1 with no natural rows).
  • lostCause: DropLoss | null — Why the game was lost, or null.

DropGenerator

type DropGenerator = | { /** Random colours from the seed. */ kind: "random"; /** Probability a cell repeats the colour of the piece it pushes down: higher makes vertical runs commoner. */ repeat: number; } | { /** An authored strip of rows that loops, starting at a random row (the arcade's looping 64-row strips). */ kind: "strip"; /** Rows, top to bottom, `cols` characters each, through the legend. */ rows: string[]; } | { /** No natural rows (puzzle boards). */ kind: "none"; }

Where natural rows come from.

DropHeldState

interface DropHeldState

A piece in the clown's hand.

  • id: number — Stable id.
  • kind: DropKind — Kind.
  • colour: number — Colour index or −1.
  • dir: ArrowDir | null — Arrow direction, or null.
  • count: number — Bomb count (0).
  • y: number — Displayed row while flying down to the clown; equals rows (the clown's line) once held.
  • fromCol: number — The column it was taken from (for drawing its flight).

DropKind

type DropKind = "normal" | "special" | "rainbow" | "ice" | "colour-ice" | "bubble" | "arrow" | "bomb"

Piece kinds. See the docs for each kind's grab, match and clear rules.

DropLevel

interface DropLevel

One level row: how fast rows arrive and what they contain.

  • pushInterval: number — Seconds between natural row pushes while the board is idle.
  • specials?: DropChance[] — Special pieces mixed into generated rows at this level (default none).
  • repeat?: number — Probability that a generated cell repeats the colour of the piece it pushes down (default: the generator's).

DropLoss

type DropLoss = "push" | "garbage" | "throw" | "limit" | "time"

Why a game was lost.

DropObjective

type DropObjective = | { /** Survive: levels advance every `levels.every` pieces; the game ends only when the stack overflows. */ kind: "endless"; } | { /** Clear `count` pieces to finish the level; the next level starts on a fresh board (the versus quota rule played solo). */ kind: "quota"; /** Pieces to clear per level. */ count: number; } | { /** Clear as many pieces as possible before the clock runs out ("Time Crush"). */ kind: "time"; /** Length of the game in seconds. */ seconds: number; } | { /** Clear every piece from an authored board (puzzle stages). */ kind: "clear"; }

The round objective.

DropPhase

type DropPhase = "rest" | "rise" | "throw" | "push" | "clearing"

A piece's presentation phase.

DropPieceState

interface DropPieceState

A piece on the board. Ids are stable for the piece's whole life (board → hand → board).

  • id: number — Stable id.
  • kind: DropKind — Kind.
  • colour: number — Colour index, or −1 for colourless kinds.
  • dir: ArrowDir | null — Arrow direction, or null.
  • count: number — Bomb count (0 for other kinds).
  • col: number — Column.
  • row: number — Logical row from the ceiling (0 = top).
  • y: number — Displayed row (float): equals row at rest; between rows while rising, flying or sliding in.
  • phase: DropPhase — What it is doing.
  • clear: number — Clearing progress 0..1 (0 unless clearing).
  • chain: number — The chain link that cleared it (0 unless clearing).
  • silent: boolean — A bubble popping silently (no score) rather than a scored clear.

DropPuzzle

interface DropPuzzle extends DropStage

A Drop Puzzle stage with its par (the fewest throws that clear it).

  • name: string — Display name.
  • par: number — Fewest throws that clear the board.
  • solution: [number[], number][] — One solution: pairs of [grab column(s), throw column], for tests and hints.

DropPuzzleOptions

interface DropPuzzleOptions

Options for dropPuzzlePreset.

  • difficulty?: DropDifficultyName — Easy (+2 throws, no clock), Normal (+1, 120 s) or Arcade (par, 60 s).
  • stage?: number — First stage (1-based).
  • stages?: readonly DropPuzzle[] — Stages to play (default DROP_PUZZLES).
  • cols?: number — Columns (default 8).
  • rows?: number — Rows (default 10).
  • wrap?: boolean — Clown wraps edge to edge (default true).

DropRepeat

interface DropRepeat

Auto-repeat timing for a held direction.

  • delay: number — Seconds a direction must be held before it repeats.
  • rate: number — Seconds between repeats after that.

DropRules

interface DropRules

Rule variations across the series.

  • matchLength: number — Pieces in a vertical line needed to clear (the series: 3).
  • specialMatch: "separate" | "with-normals" — separate: special pieces only match other specials (MD1). with-normals: they match normals of their colour (MD2+).
  • specialGrab: "separate" | "with-normals" — separate: specials cannot be held with normals (MD1, MD2). with-normals: grabbed together (MD3).
  • bubblePopOnGrab: boolean — Grabbing a bubble pops it (Saturn/PS arrangements and later); otherwise bubbles block the column (NEOGEO).
  • manualPush: boolean — The player may push a row in at any time (MD3's C button).
  • throwOverflow: "lose" | "refuse" — A throw that would push the column past the bottom: lose (the series) or refuse.
  • thawKeepsKind: boolean — A special or rainbow clear that takes in ice turns all plain ice into specials/rainbows (MD3); otherwise normals.
  • handLimit: number — Most pieces the clown may hold (0 = unlimited, as in the series).

DropRuleSet

type DropRuleSet = "md1" | "md2" | "md3"

Series rule sets.

DropScoring

interface DropScoring

How a finished chain scores. Defaults are Magical Drop III's, measured by maruta (see the docs).

  • perPiece: number — Points per piece cleared during the chain (MD3: 2). Bubbles that pop silently score nothing.
  • chainBase: number — Bonus for a 2-chain (MD3: 24); each further link doubles it up to doubleUntil.
  • doubleUntil: number — The last chain length that doubles (MD3: 13, worth 24 × 2^11).
  • linearStep: number — Past doubleUntil, each extra link is worth this many more points (MD3: 50,000 × (n − 13)).

DropSpec

interface DropSpec

A piece description, as authored in maps, strips, generators and garbage lines.

  • kind?: DropKind — The kind; default normal.
  • colour?: number — Colour index 0..colours−1 for normal, special and colour-ice; ignored (−1) for the others.
  • dir?: ArrowDir — Arrow direction (arrows only).
  • count?: number — Bomb count (bombs only, ≥ 1).

DropStage

interface DropStage

An authored puzzle stage for the clear objective.

  • name?: string — Display name.
  • board: string[] — Rows, top to bottom, through the legend. Columns must hang from the ceiling without gaps.
  • throws?: number — Throw limit for this stage (0 = unlimited). Overrides limits.throws.
  • seconds?: number — Time limit in seconds (0 = none). Overrides limits.seconds.

DropStatus

type DropStatus = "ready" | "playing" | "clear" | "lost" | "complete"

Game status.

DropTiming

interface DropTiming

Timing, in seconds or rows per second.

  • pop: number — How long a cleared piece stays as a flashing "clock" before vanishing (MD3: 0.667 s, fixed).
  • riseBase: number — Fixed part of the time pieces take to rise into a gap (MD3: about 0.2 s).
  • risePerRow: number — Extra rise time per row of gap (MD3: about 0.05 s; 1 row 0.25 s, 3 rows 0.35 s, 5 rows 0.45 s).
  • throwSpeed: number — Thrown pieces fly up at this many rows per second (MD3 footage: about 9 rows in 0.12 s).
  • grabSpeed: number — Grabbed pieces fly down to the clown at this many rows per second (presentation only).
  • pushSlide: number — Time a pushed row takes to slide in (presentation only; the push itself is instant).
  • garbageLine: number — Seconds between incoming garbage lines while they enter.
  • ready: number — READY time before play.
  • clear: number — Time the level-clear banner shows before the next level or stage.

GarbageLine

type GarbageLine = (DropSpec | null)[]

One garbage line: a piece per column, or null where that column is not pushed (MD3's uneven attack shapes).

GrabThrowColumnsApi

type GrabThrowColumnsApi = typeof api

game.grabThrowColumns: every function, class and type this pack carries, as one namespace.

MagicalDropMode

type MagicalDropMode = "endless" | "quota" | "time"

Single-player modes of the Magical Drop preset.

MagicalDropOptions

interface MagicalDropOptions

Options for magicalDropPreset.

  • mode?: MagicalDropMode — endless (survival), quota (levels cleared by quota on fresh boards) or time (clear the most in the time).
  • rules?: DropRuleSet — Rule set: md1 (1995), md2 (1996) or md3 (1997, default).
  • difficulty?: DropDifficultyName — Easy, Normal (default) or Arcade.
  • level?: number — Start level (default 1).
  • seed?: number — Seed.
  • cols?: number — Columns (default 7).
  • rows?: number — Rows (default 12; 14 for md1).
  • colours?: number — Colours (default 4).
  • startRows?: number — Rows on the board at the start of a level (default 5).
  • wrap?: boolean — Clown wraps from edge to edge (default: md2's survival field does; others do not).
  • quota?: number — Quota per level (quota mode; default from the difficulty).
  • seconds?: number — Game length in seconds (time mode; default 120).
  • levelEvery?: number — Pieces per level-up in endless mode (default from the difficulty).
  • pushScale?: number — Override the push-interval scale (default from the difficulty).