Phaser AE

Drop tower

Pro packv3.0.0-3Arcade

A ball bouncing down a spinning tower of platforms, through gaps or smashing through. Two presets cover the two classic ways to play, and a seeded generator builds towers and proves each one can be won.

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

Released October 14, 2026 · updated October 19, 2026

Drop tower: 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

dropTower

function dropTower():Pack<{dropTower:typeof kit}>

Install the public rules and seeded generator, independently of rendering.

Helpers

createDropTower

const createDropTower = (config: TowerConfig, controls: TowerControls = {})=> …

Create a tower from config, played with controls (see TowerControls; the defaults are the reference game's). Throws if a value is outside its range, the preset or a sector kind is unknown, or the layers do not descend.

generateDropTower

function generateDropTower(o: TowerOptions = {}): TowerLevel

Generate a tower for o and certify it by replaying a winning solution; medium and harder towers must also defeat idle play. Throws if an option is out of range, or if no tower certifies within 80 attempts.

towerPhysics

function towerPhysics(preset: 'helix' | 'smash'): TowerPhysics

The physics generateDropTower uses for a preset when TowerOptions.physics leaves a field out.

Classes

DropTower

class DropTower

A drop-tower round: continuous vertical flight of a bouncing ball and angular-footprint contacts with turning platforms. Rules only; it draws nothing. Build one with createDropTower.

  • readonly config: TowerConfig & TowerPhysics — The validated tower configuration: a copy of the one passed in, with the optional physics filled in.
  • readonly controls: Required<TowerControls> — The pointer controls in force, defaults filled in.
  • constructor(c: TowerConfig, controls: TowerControls = {})
  • get state() — A copy of the current state; changing it does not affect play.
  • get solved() — Whether the ball has passed every platform without losing.
  • get lost() — Whether a hazard has ended the round.
  • sector(layer = this.data.layer, time = this.data.time, rotation = this.data.rotation): TowerSector — What lies under the ball's footprint on platform layer at time seconds with the tower turned rotation radians; each defaults to now. Samples the centre and both edges of the footprint: 'gap' if all three are open or the layer does not exist, 'hazard' if any is a hazard, otherwise 'safe'.
  • rotate(delta: number) — Turn the tower by delta radians. Only turns in the helix preset while the round is live; otherwise it does nothing. Throws unless delta is finite and within -2π to 2π.
  • hold(value: boolean) — Set whether the hold input is down. In smash, while held the ball dives at 7 world units per second and breaks safe platforms, but a hazard then ends the round.
  • checkpoint() — Mark the present state as somewhere undo can return to (an undo point, added to the automatic one before each landing). Not the same as the automatic checkpoints every checkpointEvery layers, which retryCheckpoint restores.
  • press() — The player puts a finger or the mouse down: an undo point (unless controls.undoOnPress is false), and in smash the hold goes down.
  • drag(dx: number) — The pressed pointer moved dx world units across: in helix the tower turns dx × controls.dragTurn radians, at most controls.maxDragTurn a call. Throws unless dx is finite.
  • release() — The player lets go: in smash the hold comes up.
  • addLayers(layers: readonly TowerLayer[]): number — Append platforms below the last one, for an endless or streamed descent: each must be more than 0.15 below the one before, the first more than 0.15 below the current last layer. Append before the ball passes the last platform; a solved tower becomes live again and the ball falls on. Returns the new number of layers. Throws on a value out of range, layers that do not descend, or more than 10000 layers in all.
  • step(dt: number) — Advance the simulation by dt seconds in fixed 1/120-second ticks; any remainder carries over to the next call. Throws unless dt is finite and 0 to 0.1.
  • hint() — The demonstration controller's input for the next 1/120-second step, { rotate, hold }, or null once the round is solved or lost. It uses the same bounded rotation (at most turnSpeed / 120 radians a step) and hold input as a player: helix turns towards the nearest gap that will be under the ball when it lands; smash holds unless a hazard is under the ball now or will be when it lands.
  • undo() — Return to the last undo point: just before the most recent landing, or the last checkpoint(). From the live present the state undo leaves is kept, so redo can return to it.
  • redo() — Return to the state undo left. Returns false when there is nothing to redo.
  • reset() — Start again from the top: initial state, no checkpoint and no undo history.
  • retryCheckpoint() — Return to the latest automatic checkpoint (the start if there is none), with the ball startHeight above that platform and the score and streak as they were then. Clears the undo history.

Types

DropTowerApi

type DropTowerApi=typeof kit

Public generator and rule constructors exposed on composed games.

TowerConfig

interface TowerConfig

A complete tower: the platforms, the ball and the physics. Pass it to createDropTower; generateDropTower builds one.

  • preset: 'helix' | 'smash' — 'helix': turn the tower to bring a gap under the ball; a hazard ends the round. 'smash': turning is disabled, holding dives the ball and breaks safe platforms, and a hazard hit while holding ends the round.
  • layers: TowerLayer[] — Platforms from the top down, 1 to 10000 of them, each lower than the last. addLayers appends more during play up to the same bound, which only keeps the per-read state copy and the undo snapshots small.
  • gravity: number — Downward acceleration in world units per second squared; 2 to 40.
  • bounce: number — Upward speed in world units per second the ball gets when it bounces off a platform; 1 to 10.
  • ballRadius: number — Ball radius in world units, 0.05 to 0.4. The ball rests with its centre this far above a platform.
  • trackRadius: number — Distance of the ball from the tower axis in world units, 1 to 4. With ballRadius it sets the ball's angular footprint of plus or minus asin(ballRadius / trackRadius) radians about its centre.
  • turnSpeed: number — Fastest turn, in radians per second, that hint() asks for; 0.2 to 12. rotate itself accepts any single turn of up to a full circle.
  • smashAfter: number — Consecutive descents (through gaps or smashed platforms) that charge a power smash, a whole number from 2 to 10. Once the streak reaches this, the next platform breaks whatever its sectors.
  • checkpointEvery: number — Layers between automatic checkpoints for retryCheckpoint, a whole number from 0 to 20; 0 turns them off.
  • maxFallSpeed?: number — Fastest the ball falls, in world units per second, 1 to 40; default 8 (its terminal speed under gravity).
  • diveSpeed?: number — Speed the ball dives at while the hold input is down in the smash preset, world units per second, 1 to 40; default 7.
  • startHeight?: number — Height of the ball's centre above the top platform at the start, and above the checkpoint platform after retryCheckpoint, in world units; more than ballRadius and at most 20; default 1.4.

TowerControls

interface TowerControls

How a player's pointer plays a tower, for press, drag and release. Every field is optional.

  • dragTurn?: number — Radians the tower turns per world unit of horizontal drag, in helix; above 0. Default 0.012.
  • maxDragTurn?: number — The most one drag call turns the tower, radians, 0 to 2π. Default 1.
  • undoOnPress?: boolean — Whether each press marks an undo point, so undo returns to the start of that gesture. Default true.

TowerLayer

interface TowerLayer

One horizontal platform: a ring of equal angular sectors that turns about the tower axis.

  • y: number — Height of the platform's top surface in world units, any finite value. Layers must descend: each is more than 0.15 below the one before. Generated towers start at 0 and go down.
  • phase: number — Angle in radians the sector pattern is turned at time 0, added to the player's rotation and the layer's spin; -100 to 100.
  • spin: number — Angular speed of the platform in radians per second, added to the player's rotation; the sign sets the direction and 0 holds it still. -4 to 4.
  • radius: number — Outer radius of the platform in world units, from trackRadius + ballRadius to 8. The contact rules ignore it; it is for presentation.
  • shape: 'circle' | 'square' | 'hexagon' — Outline of the platform: 'circle', 'square' or 'hexagon'. The contact rules ignore it; it is for presentation.
  • sectors: TowerSector[] — The platform's equal angular slices in order around the ring, from 8 to 40 of them.

TowerLevel

interface TowerLevel

A generated, certified tower with the settings and measurements that describe it.

  • seed: number — The seed the level was generated from.
  • difficulty: Difficulty — The difficulty the level was generated for.
  • config: TowerConfig — The tower to play; pass it to createDropTower.
  • parameters: Required<TowerOptions> — Every option with its default filled in. Pass it back to generateDropTower to reproduce this level.
  • solution: { /** Radians to pass to \rotate` for this step. / rotate: number; /* Value to pass to `hold` for this step. */ hold: boolean; }[]` — A winning performance replayed during generation: one entry per 1/120-second step, giving the turn and hold input for that step.
  • metrics: { /** * Layout family: 'helix' for the helix preset; for smash one of 'cone', 'hourglass', 'drums', 'corkscrew', * 'split-sectors' or 'alternating-bands', chosen by seed. */ family: string; /** Number of platforms. */ floors: number; /** Hazard sectors in the whole tower. */ hazardSectors: number; /** Gap sectors in the whole tower; always 0 for smash. */ gapSectors: number; /** * Platforms whose phase differs from the one above by more than 0.1 radians, so the player has to turn between * them. */ rotationChanges: number; /** Seconds the certifying solution takes to finish the tower. */ duration: number; /** * Platforms cleared by idle play (no turning; held throughout in smash). Medium and harder levels are only * kept when this stays at or below 65% of the floors. */ idleCleared: number; /** * JSON fingerprint of each platform's shape, radius, spacing, spin direction, sectors and phase, rounded, used * to tell towers apart. */ structure: string; } — Measurements of the generated level.

TowerOptions

interface TowerOptions

Controls for generateDropTower. Every field is optional and defaults as described; a value outside its range throws.

  • seed?: number — Unsigned 32-bit integer, zero included, that fixes the layout; default 1.
  • difficulty?: Difficulty — 'easy', 'medium', 'hard' or 'very-hard'; sets the defaults for floors, gap width, hazards and spin and how far each layer turns; default 'medium'.
  • preset?: 'helix' | 'smash' — 'helix' (turn to find gaps) or 'smash' (hold to break safe platforms); default 'helix'.
  • floors?: number — Number of platforms, 3 to 50; default 9, 14, 20 or 28 by difficulty, plus seed % 3.
  • sectors?: number — Sectors per platform, 12 to 32; default 24.
  • gapWidth?: number — Consecutive gap sectors per helix platform, from 2 up to half of sectors; default 7, 5, 4 or 3 by difficulty. Smash cuts no gaps, but the value still limits how many hazards fit.
  • hazards?: number — Hazard sectors per platform, from 1 to sectors - gapWidth - 2; default 3, 6, 9 or 12 by difficulty.
  • spin?: number — Platform spin in radians per second, 0 to 2.5. Default for helix is 0 on easy and medium, 0.3 on hard and 0.45 on very-hard; for smash 0.65, 0.95, 1.35 or 1.7 by difficulty.
  • shape?: 'auto' | 'circle' | 'square' | 'hexagon' — Platform outline: 'auto' picks one from the seed (and the section, in the alternating-bands family), or force 'circle', 'square' or 'hexagon'; default 'auto'.
  • physics?: Partial<TowerPhysics> — Ball and physics settings for the generated tower; each field left out takes towerPhysics(preset)'s value (gravity 14, bounce 4.8 for helix and 2.8 for smash, ball radius 0.16, track radius 2.25, turn speed 6, smash after 3, checkpoint every 5, max fall speed 8, dive speed 7, start height 1.4). Platform radii follow the track and ball radius. The level is certified with these settings, so a combination its controller cannot win throws.

TowerPhysics

type TowerPhysics = Required<Pick<TowerConfig, 'gravity' | 'bounce' | 'ballRadius' | 'trackRadius' | 'turnSpeed' | 'smashAfter' | 'checkpointEvery' | 'maxFallSpeed' | 'diveSpeed' | 'startHeight'>>

The ball and physics settings of a tower, every one filled in: what TowerOptions.physics sets for a generated tower and what generateDropTower records in parameters.physics. See TowerConfig for each field's meaning and range.

TowerSector

type TowerSector = 'safe' | 'hazard' | 'gap'

One angular slice of a platform: 'safe' is solid, 'hazard' is solid and ends the round when hit (always in helix, only while holding in smash), 'gap' is open so the ball falls through.

TowerState

interface TowerState

Everything that changes during play: the clock, the tower's rotation, the ball, the streak and score, and the outcome.

  • time: number — Seconds simulated since the start or the last reset, in steps of 1/120.
  • rotation: number — The player's tower rotation in radians, wrapped to 0 up to 2π. Stays 0 in the smash preset.
  • y: number — Height of the ball's centre in world units, on the same axis as TowerLayer.y; starts startHeight (default 1.4) above the top platform.
  • vy: number — Vertical velocity in world units per second; negative is falling. Falling is capped at maxFallSpeed (default 8), and holding in smash sets it to minus diveSpeed (default 7).
  • layer: number — Index of the platform the ball is falling towards; equals the number of layers once the tower is solved.
  • streak: number — Consecutive descents since the last bounce, counting towards a power smash; reset by a bounce and after a power smash.
  • bestStreak: number — Highest streak reached this round.
  • score: number — Points so far: each descent adds 10 times (1 plus the streak after it).
  • lost: boolean — Whether a hazard has ended the round.
  • holding: boolean — Whether the hold input is down, as set by hold.
  • broken: number[] — Indices of the platforms the ball smashed through, as opposed to passing through a gap.
  • last: string — Short message about the latest event, such as 'Bounced' or 'Power smash!', for display; 'Ready' before play starts.
  • checkpoint: number — Index of the layer the latest automatic checkpoint was taken at; 0 before the first.