Phaser AE

Rope-cutting puzzles

Free packv3.0.0-12DPhysics

The rules of a rope-cutting physics puzzle, played on real rope physics. Candies hang from ropes; the player swipes to cut them, and each candy swings, falls, floats or is puffed into a mouth, collecting stars on the way. Levels are plain data, and a game can add elements of its own.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 19, 2026 · updated October 20, 2026

Rope-cutting puzzles: 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

cutRope

function cutRope(): Pack<{ cutRope: CutRopeApi }>

The cut-rope pack: game.cutRope, Cut the Rope's rules on a rope world of the game's own per level.

Functions

alongPath

function alongPath(points: ReadonlyArray<RopeVec>, distance: number, mode: 'pingpong' | 'loop' = 'pingpong'): RopeVec

The point distance along a polyline, going back and forth ('pingpong') or round it ('loop').

createCutRope

function createCutRope(level: CutRopeLevel, options: CutRopeOptions = {}): CutRopeGame

Make a game of a level. Throws, listing every problem, on a level validateCutRopeLevel refuses.

validateCutRopeLevel

function validateCutRopeLevel(level: CutRopeLevel, kinds: ReadonlyArray<string> = []): string[]

Check a level and say what is wrong with it, as plain sentences; empty when it can be played.

Constants

cutRopeDefaults

const cutRopeDefaults: { readonly candyRadius: 16; readonly candyMass: 10; readonly ropeMass: 0.5; readonly ropeRadius: 3; readonly slack: 0.05; readonly mouthRadius: 32; readonly starRadius: 18; readonly bubbleRadius: 34; ... 7 more ...; readonly margin: 60; } …

The defaults every level element falls back on.

cutRopeLevels

const cutRopeLevels: { readonly classic: readonly CutRopeLevel[]; readonly tower: readonly CutRopeLevel[] }

The built-in levels, by box.

Types

CutRopeApi

interface CutRopeApi

What the cut-rope pack adds to a game: game.cutRope.

  • create: typeof createCutRope — Make a game of a level.
  • validate: typeof validateCutRopeLevel — Check a level; empty when it can be played.
  • levels: typeof cutRopeLevels — The built-in box of levels, a preset to start from.
  • defaults: typeof cutRopeDefaults — The defaults level elements fall back on.
  • alongPath: typeof alongPath — A point along a hook's path.

CutRopeBubble

interface CutRopeBubble

A bubble: a candy that drifts into it floats away from gravity at rise units per second until it is tapped.

  • x: number — Where it is, across.
  • y: number — Where it is, down.
  • radius?: number — Its radius. Default 34.
  • rise?: number — How fast it floats, units per second. Default 150.

CutRopeCandy

interface CutRopeCandy

A candy: what the ropes hold and a mouth eats.

  • id: string — Its id, unique in the level.
  • x: number — Where it starts, across.
  • y: number — Where it starts, down.
  • radius?: number — Its radius. Default 16.
  • mass?: number — Its mass, against the ropes' (each rope weighs 0.5). Default 10.

CutRopeContext

interface CutRopeContext

What a registered element can reach.

  • readonly world: RopeWorld — The rope world: ropes, loads, colliders.
  • readonly candies: ReadonlyMap<string, RopeLoad> — The candies still in play, by id.
  • readonly level: Readonly<CutRopeLevel> — The level being played.
  • readonly state: Readonly<CutRopeState> — The game's state.
  • emit(name: string, detail?: unknown): void — Tell the presenter something happened: an element event from this element.
  • lose(candy: string): void — Lose the game: this candy is destroyed.

CutRopeCushion

interface CutRopeCushion

An air cushion: tapping it puffs every candy in front of it, within reach and spread radians of angle, with an impulse of strength (mass times units per second) at the cushion that falls off to 40% at reach.

  • x: number — Where it is, across.
  • y: number — Where it is, down.
  • angle: number — Which way it blows, radians (0 is +x, y down).
  • strength?: number — The impulse at the cushion's mouth. Default 4000: a 10-mass candy at 400 units per second.
  • reach?: number — How far the puff reaches. Default 260.
  • spread?: number — Half the puff's angle, radians. Default 0.5.
  • size?: number — How near a tap must be to puff it. Default 40.

CutRopeElement

interface CutRopeElement

An element of a kind the game registered: kind names it, and the rest is that kind's data.

  • kind: string — The registered kind's name.
  • [key: string]: unknown — The kind's own data.

CutRopeElementKind

interface CutRopeElementKind<S = unknown>

A kind of element a game adds to the level format: a teleport, a fan, a moving spike. One instance per level entry of that kind. Its state must be plain JSON, so saves can carry it.

  • create(entry: CutRopeElement, ctx: CutRopeContext): S — Make an instance's state from its level entry.
  • step?(state: S, ctx: CutRopeContext, dt: number): void — Run every step, after the ropes moved and the built-in rules ran.
  • tap?(state: S, ctx: CutRopeContext, point: RopeVec): boolean — A tap at a point. Return true when this element took it, so nothing else does.
  • view?(state: S, ctx: CutRopeContext): unknown — What a presenter draws for it.

CutRopeEvent

type CutRopeEvent = | { kind: 'cut'; rope: string; cause: 'swipe' | 'tear' | 'call' } | { kind: 'star'; star: number; candy: string } | { kind: 'star-gone'; star: number } | { kind: 'tie'; pin: number; candy: string; rope: string } | { kind: 'bubble'; bubble: number; candy: string } | { kind: 'pop'; bubble: number; candy: string } | { kind: 'puff'; cushion: number } | { kind: 'gravity'; switch: number; down: boolean } | { kind: 'eat'; mouth: number; candy: string } | { kind: 'lost'; candy: string; cause: 'hazard' | 'out' } | { kind: 'won'; stars: number } | { kind: 'element'; element: number; name: string; detail?: unknown }

What happened, for the presenter to show and sound.

CutRopeGame

interface CutRopeGame

A game of Cut the Rope on one level.

  • readonly level: Readonly<CutRopeLevel> — The level, as played (a copy of the one given).
  • readonly state: Readonly<CutRopeState> — The state, live.
  • readonly world: RopeWorld — The rope world, for drawing or for a registered element. A restart or a restore replaces it.
  • step(dt: number): void — Advance by dt seconds: the ropes, then the rules. Does nothing once the game is won or lost.
  • swipe(path: ReadonlyArray<RopeVec>): number — Cut every rope the polyline crosses (this step or the last). Returns how many ropes it cut.
  • tap(point: RopeVec): string | null — A tap: pop a bubble, puff a cushion, flip gravity, or whatever a registered element takes. What took it, or null.
  • drain(): CutRopeEvent[] — What happened since the last drain, oldest first.
  • view(): CutRopeView — Everything to draw.
  • restart(): void — Start the level again.
  • snapshot(): CutRopeSave — The game as plain JSON, to resume later on the same level.
  • restore(save: CutRopeSave): void — Resume a snapshot of this level. Throws on one of another level, and the game is left as it was.

CutRopeHook

interface CutRopeHook

A hook a rope hangs from: still, sliding along a path, or going round a centre.

  • x: number — Where it is across: the hook itself, or the centre of an orbit.
  • y: number — Where it is down.
  • path?: { points: ReadonlyArray<RopeVec>; speed: number; mode?: 'pingpong' | 'loop'; offset?: number } — Slide along these points at speed units per second, back and forth ('pingpong', the default) or round and round ('loop'), starting offset units along.
  • orbit?: { radius: number; speed: number; phase?: number } — Go round (x, y) at radius, speed turns per second (negative is anticlockwise), starting at phase turns.

CutRopeLevel

interface CutRopeLevel

A level. Units are the game's, 2D with y down; a candy that leaves the play area by margin is lost.

  • id?: string — Its id, which saves carry.
  • name?: string — A name to show.
  • width: number — The play area's width, from x = 0. It may be larger than the screen: a tall or wide level scrolls.
  • height: number — The play area's height, from y = 0.
  • margin?: number — How far past the play area a candy may go before it is lost. Default 60.
  • gravity?: number — Gravity, units per second squared, down. Default 900.
  • candies: CutRopeCandy[] — The candies, at least one.
  • mouths: CutRopeMouth[] — The mouths, at least one. The level is won when every candy is eaten.
  • ropes: CutRopeRope[] — The ropes the level starts with (rope pins add more).
  • stars?: CutRopeStar[] — Stars to collect, for a rating.
  • pins?: CutRopePin[] — Rope pins.
  • bubbles?: CutRopeBubble[] — Bubbles.
  • cushions?: CutRopeCushion[] — Air cushions.
  • switches?: CutRopeSwitch[] — Gravity switches.
  • hazards?: RopeCollider[] — Shapes that destroy a candy that touches them: spikes, saws, electricity.
  • walls?: RopeCollider[] — Solid shapes candies bounce off and ropes drape over.
  • elements?: CutRopeElement[] — Elements of kinds the game registered.

CutRopeMouth

interface CutRopeMouth

A mouth (Om Nom): a candy that reaches it is eaten.

  • x: number — Where it is, across.
  • y: number — Where it is, down.
  • radius?: number — How close a candy's centre must come, plus half the candy's radius. Default 32.
  • candy?: string — Eat only this candy: two mouths, two candies, each its own colour. Default any candy.
  • angle?: number — Which way it faces, for drawing: radians, -Math.PI / 2 (up, the default) sits on a ledge, Math.PI / 2 hangs.

CutRopeOptions

interface CutRopeOptions

How a game is made.

  • elements?: Record<string, CutRopeElementKind<unknown>> — Element kinds beyond the built-in ones, by the kind level entries use.
  • rope?: Omit<RopeWorldOptions, 'gravity' | 'flat'> — The rope world's options: step, iterations, substeps, damping. Gravity comes from the level.

CutRopePin

interface CutRopePin

A rope pin: when a candy comes within reach, a rope of that length ties itself from the pin to the candy, once.

  • x: number — Where it is, across.
  • y: number — Where it is, down.
  • reach: number — How near the candy must come; also the new rope's length unless length says otherwise.
  • length?: number — The new rope's length. Default reach.

CutRopeRope

interface CutRopeRope

A rope from a hook to a candy.

  • id?: string — Its id, for events and hints. Default rope-<index>.
  • hook: CutRopeHook — What it hangs from.
  • candy?: string — The candy it holds. Default the level's first candy.
  • length?: number — Its length. Default the distance from the hook to the candy, times 1 + slack.
  • slack?: number — Extra length as a fraction of the distance, when length is not given. Default 0.05: just slack enough to hang.
  • radius?: number — Its thickness, a radius. Default 3.

CutRopeSave

interface CutRopeSave

A saved game.

  • format: 'phaser-ae-cut-rope' — Always 'phaser-ae-cut-rope'.
  • version: 1 — The save format's version, 1.
  • level: string — The level's id ('' for a level without one).
  • state: CutRopeState — The game's state.
  • world: RopeSnapshot — The rope world.
  • holds: [string, string][] — Which candy each rope in the world holds.
  • pinsUsed: boolean[] — Which rope pins have tied their rope.
  • bubbles: { candy: string | null; popped: boolean }[] — What each bubble holds, and whether it has popped.
  • puffs: (number | null)[] — When each cushion last puffed, level seconds, or null.
  • elements: unknown[] — Each registered element's state.

CutRopeStar

interface CutRopeStar

A star, collected when a candy passes through it.

  • x: number — Where it is, across.
  • y: number — Where it is, down.
  • radius?: number — Its radius. Default 18.
  • timer?: number — A timed star: it is gone this many seconds into the level. Default never.

CutRopeState

interface CutRopeState

The game's state.

  • phase: 'playing' | 'won' | 'lost' — 'playing' until every candy is eaten ('won') or one is lost ('lost').
  • time: number — Seconds played.
  • stars: number — Stars collected.
  • taken: boolean[] — Which stars are collected, in level order.
  • eaten: string[] — The candies eaten, by id.
  • lost: { candy: string; cause: 'hazard' | 'out' } | null — How a lost game was lost.
  • down: boolean — Whether gravity points down (it flips with a switch).

CutRopeSwitch

interface CutRopeSwitch

A gravity switch: tapping it turns gravity upside down, and back.

  • x: number — Where it is, across.
  • y: number — Where it is, down.
  • size?: number — How near a tap must be. Default 32.

CutRopeView

interface CutRopeView

Everything a presenter draws, in level units.

  • candies: { id: string; x: number; y: number; vx: number; vy: number; radius: number; bubble: number | null; eaten: boolean }[] — Every candy, eaten ones too, and which bubble holds it.
  • mouths: { x: number; y: number; radius: number; angle: number; candy?: string; open: number }[] — Each mouth, and how wide open it is (0 to 1) as a candy it wants comes near.
  • ropes: { id: string; candy: string; runs: number[][]; holding: boolean; radius: number }[] — Each rope's uncut stretches as [x0, y0, z0, x1, …] (once its candy is eaten, only the one hanging from its hook), and whether it still holds its candy.
  • hooks: { rope: string; x: number; y: number; moving: boolean }[] — Where each rope's hook is now.
  • stars: { x: number; y: number; radius: number; taken: boolean; gone: boolean; left: number | null }[] — Each star: taken, gone (a timed star whose time is up), and seconds left (null for an untimed one).
  • pins: { x: number; y: number; reach: number; used: boolean }[] — Each rope pin, and whether it has tied its rope.
  • bubbles: { x: number; y: number; radius: number; candy: string | null; popped: boolean }[] — Each bubble: where it is (on its candy while it holds one), and whether it has popped.
  • cushions: { x: number; y: number; angle: number; reach: number; spread: number; size: number; puffed: number }[] — Each cushion, and seconds since it last puffed (Infinity before the first).
  • switches: { x: number; y: number; size: number }[] — Each gravity switch.
  • hazards: RopeCollider[] — The hazards, as the level gave them.
  • walls: RopeCollider[] — The walls, as the level gave them.
  • elements: { kind: string; view: unknown }[] — What each registered element's view returned, in level order.
  • down: boolean — Whether gravity points down.