Phaser AE

Stack timing

Pro packv3.0.0-3Arcade

The rules of a one-tap tower builder: a slab sweeps over the top of the tower, the player taps to drop it, the overhang is cut away and the next slab is smaller. Land inside the perfect window and it snaps on and builds a streak. It runs headless, so it suits a flat 2D side view or a full 3D tower alike.

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

Stack timing: 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

stackTiming

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

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

Helpers

createStackTiming

const createStackTiming = (config: StackConfig)=> …

Create a round from config, the same as new StackTiming(config).

generateStackTiming

function generateStackTiming(options: StackOptions = {}): StackLevel

Generate a tower from options and prove it can be built by playing it through the rules. Throws on floors, speed or tolerance outside their bounds, an unknown difficulty or pattern, a regrow or scoring value out of range, or a seed outside 0 to 4294967295, and if the replay does not finish the tower.

Classes

StackTiming

class StackTiming

A stacking round: slabs sweep across and are dropped onto the tower, trimmed to what overlaps.

  • readonly config: StackConfig — The tower it was created with (an owned copy).
  • constructor(c: StackConfig)
  • get state() — A fresh copy of the state.
  • get solved() — Whether every course has been placed.
  • get lost() — Whether a drop missed the stack and ended the round.
  • target(): StackSlab | null — Intended landing footprint; inset courses remain inside the preceding slab.
  • moving(): StackSlab | null — The sweeping slab now: the target footprint shifted along the stage's axis. Null once the round is solved or lost.
  • step(dt: number) — Advance the clock by dt seconds, moving the slab (it holds still once the round is over). Throws unless dt is from 0 to 0.1, so split longer frames.
  • drop() — Drop the moving slab. Returns { valid, reason }: invalid with 'Round finished' when the round is over, otherwise valid with reason perfect, trimmed or miss. A trimmed slab keeps only its overlap; a miss (overlap of 0.035 or less) ends the round.
  • hint() — { wait }: the seconds from now until the moving slab next crosses its target, where a drop is perfect; null once the round is over. It reads the current state and moves nothing, so stepping exactly wait and dropping replays a perfect drop.
  • undo() — Take back the last drop; false when there is nothing to undo.
  • redo() — Replay a drop that undo took back; false when there is none.
  • reset() — Rebuild the round from its tower: only the base slab, no score, history cleared.
  • addStages(stages: readonly StackStage[]): number — Append courses on top of the tower's list, for an endless tower: add more before the player reaches the last. A solved round becomes live again with the next appended course moving. Returns the new number of courses. Throws on a stage value out of range or more than 10000 courses in all.

Types

StackConfig

interface StackConfig

A whole tower: the base slab, the slab thickness, the perfect window and every course.

  • width: number — Width (x) of the base slab, world units from 0.5 to 20; each course is cut from the one below.
  • depth: number — Depth (z) of the base slab, world units from 0.5 to 20.
  • height: number — Thickness of every slab, world units from 0.1 to 2.
  • perfectTolerance: number — How far, in world units from 0.001 to 0.5, a drop may be from the target and still count as perfect and snap onto it.
  • stages: StackStage[] — The courses to place, from the bottom up: from 1 to 10000, a bound that keeps state copies and undo snapshots small. addStages appends more during play, for an endless tower.
  • regrow?: StackRegrow — Regrow the slab after a perfect streak, as in Stack; omit (the default) for no regrowth.
  • scoring?: StackScoring — Points per drop; omitted fields score as before: perfect 10 plus 2 per streak, trimmed 5.

StackLevel

interface StackLevel

A generated tower and the proof that it can be built.

  • seed: number — The seed used.
  • difficulty: Difficulty — The difficulty used.
  • config: StackConfig — The tower to play: pass it to createStackTiming.
  • parameters: Required<StackOptions> — The options with every default filled in; pass them back to generateStackTiming to get the same level.
  • solution: { /** Seconds to step from the moment the course starts moving before dropping it. */ wait: number; }[] — A winning performance: for each course in turn, the seconds to step before dropping. Replay by stepping exactly that long, then calling drop.
  • metrics: { /** The tower family the level was built from, such as 'spiral'. */ family: string; /** Number of courses. */ floors: number; /** The shortest time, in seconds, the moving slab spends inside the perfect window on any course. */ minimumPerfectWindow: number; /** Courses whose sweep axis differs from the one below. */ axisChanges: number; /** JSON fingerprint of the footprint and each course's axis, motion, coarse speed and target, to tell towers apart. */ structure: string; } — Measurements of the level.

StackOptions

interface StackOptions

Controls for generateStackTiming; every one is optional and checked against its bounds.

  • seed?: number — Unsigned 32-bit seed, 0 to 4294967295. Default 1.
  • difficulty?: Difficulty — easy, medium, hard or very-hard. Default medium.
  • floors?: number — Courses to place, a whole number from 2 to 60. Default 8, 12, 18 or 24 by difficulty, plus 0 to 2 from the seed.
  • speed?: number — Base sweep rate, world units a second, from 0.5 to 8. Default 1.8, 2.6, 3.5 or 4.5 by difficulty.
  • perfectTolerance?: number — The perfect window, world units from 0.01 to 0.3. Default 0.18, 0.12, 0.075 or 0.045 by difficulty.
  • pattern?: 'auto' | 'alternating' | 'runs' | 'mixed' | 'terraces' | 'spiral' | 'needle' | 'bridge' — The tower family: auto (default) picks one from the seed. Others are alternating, runs, mixed, terraces, spiral, needle and bridge.
  • regrow?: StackRegrow | null — Copied into the generated tower's StackConfig.regrow; default null, no regrowth.
  • scoring?: StackScoring | null — Copied into the generated tower's StackConfig.scoring; default null, the standard scores.

StackRegrow

interface StackRegrow

The Stack-style regrowth reward: after a run of perfect drops each further perfect drop widens the placed slab along the axis it swept, never past the base slab's size on that axis.

  • after: number — Perfect drops in a row, a whole number from 1 to 100, before a perfect drop starts to regrow the slab.
  • by: number — World units, 0.01 to 5, added to the slab's size on the swept axis by each regrowing perfect drop.

StackScoring

interface StackScoring

Points per drop; every field is optional and defaults as described.

  • perfect?: number — Points for a perfect drop before the streak bonus, 0 to 1000; default 10.
  • streakBonus?: number — Extra points per drop of the current perfect streak (counting this drop), added to a perfect drop, 0 to 1000; default 2.
  • trimmed?: number — Points for a trimmed drop, 0 to 1000; default 5.

StackSlab

interface StackSlab

A placed or moving slab: its centre and size in world units.

  • x: number — Centre across the width, world units.
  • y: number — Height in world units: the base slab is 0 and each course sits config.height above the one below.
  • z: number — Centre across the depth, world units.
  • width: number — Size along x, world units.
  • depth: number — Size along z, world units.

StackStage

interface StackStage

One course of the tower: how its moving slab sweeps back and forth until the player drops it.

  • axis: 'x' | 'z' — The axis the moving slab sweeps along: x (its width) or z (its depth).
  • speed: number — Sweep rate in world units a second, from 0.1 to 20: the sine motion peaks at this speed and the linear one moves at 2/π of it.
  • travel: number — Half the sweep range, world units from 1 to 30: the slab swings between -travel and +travel either side of the target.
  • phase: number — Starting phase of the sweep in radians, from -100 to 100; the slab begins at the offset sin(phase) * travel.
  • motion: 'linear' | 'sine' — sine slows towards each end; linear keeps a constant speed and turns sharply.
  • target?: { x: number; z: number; scaleX: number; scaleZ: number; } — Where this course should land, as an inset of the slab below. Default the whole slab below. scaleX and scaleZ (0.5 to 1) scale its width and depth, and x and z (-1 to 1) slide the smaller footprint within the old one, -1 and 1 being flush with its edges.

StackState

interface StackState

The round's state, an owned copy; StackTiming.state makes a fresh one each read.

  • time: number — Seconds stepped since the round started or was reset.
  • stageTime: number — Seconds since the current slab started to move; back to 0 at every drop.
  • placed: StackSlab[] — The slabs set so far, the base first; the next course to place is config.stages[placed.length - 1].
  • offcuts: (StackSlab & { born: number; vx: number; vz: number; })[] — The trimmed overhangs, for the renderer: each carries born, the time it was cut, and a sideways drift velocity (vx, vz). They never affect later contacts.
  • streak: number — Consecutive perfect drops now; a trimmed drop or a miss resets it to 0.
  • bestStreak: number — The longest streak this round.
  • score: number — Points: by default a perfect drop scores 10 plus twice the streak so far (counting that drop), a trimmed one 5, a miss 0; StackConfig.scoring changes the amounts.
  • lost: boolean — Whether a drop missed the stack and ended the round.
  • last: 'ready' | 'perfect' | 'trimmed' | 'miss' — How the last drop went: ready before the first, then perfect, trimmed or miss.

StackTimingApi

type StackTimingApi=typeof kit

Public generator and rule constructors exposed on composed games.