~3 min read

Tweens

Phaser 4's tween system — interpolating any numeric property over time. Targets, easing, chaining, value tweens, and the pitfalls.

A tween interpolates a value over time. You point a tween at one or more objects and tell it what properties to change, how, and over what duration — Phaser handles the per-frame math.

Tweens are the single most-used utility in Phaser code: fades, eased camera moves, UI bounce, dialog reveals, value animations driving custom logic.

The shape of a tween

this.tweens.add({
	targets: sprite,
	x: 400,
	duration: 1000,
});

Reads as: “move sprite.x to 400 over 1000ms, using the default easing.” Phaser captures the current value of sprite.x at the moment the tween starts and interpolates from there.

Targets

A tween can drive any number of objects at once:

targets: sprite                              // one object
targets: [s1, s2, s3]                        // several
targets: group.getChildren()                 // a group's members
targets: { value: 0 }                        // an arbitrary plain object — see "Value tweens" below

Properties

Any numeric property is fair game. Phaser interpolates from the current value to the target.

this.tweens.add({
	targets: sprite,
	x: '+=50',                       // RELATIVE — adds 50 to the current value
	alpha: 0,                        // tween to 0
	scale: { from: 2, to: 1 },       // explicit start + end
	angle: { value: 360, ease: 'Cubic.Out' },  // per-property override
	duration: 600,
});

Relative targets ('+=50', '-=50', '*=2') resolve at start time — useful when the absolute target depends on whatever value the property happens to have when the tween fires.

Easing

ease: 'Linear'           // default if omitted is also Linear-ish
ease: 'Sine.InOut'
ease: 'Cubic.Out'
ease: 'Bounce.Out'
ease: 'Back.InOut'
ease: 'Elastic.Out'

The full set lives under Phaser.Math.Easing.*. Each family has .In, .Out, and .InOut variants. Pick Out for natural-feeling arrivals (objects decelerate into their target); In for departures; InOut for symmetric motion.

Duration, delay, repeat, yoyo

this.tweens.add({
	targets: sprite,
	y: '+=50',
	duration: 400,
	delay: 200,        // wait 200ms before starting
	yoyo: true,        // play forward, then backward
	repeat: -1,        // -1 = forever; positive N = N additional plays
	repeatDelay: 100,
});

yoyo: true doubles the effective duration of one cycle — a 400ms tween becomes 800ms forward+back. hold: N pauses for N ms at the yoyo point if you want a beat.

Callbacks

this.tweens.add({
	targets: sprite,
	x: 400,
	duration: 1000,
	onStart:     () => this.sound.play('whoosh'),
	onUpdate:    (_tween, target) => { /* every frame */ },
	onYoyo:      () => { /* hit the yoyo point */ },
	onRepeat:    () => { /* started a new cycle */ },
	onComplete:  () => sprite.destroy(),
});

For “play once and tear down,” onComplete: () => target.destroy() is the cleanest pattern.

A live example

Five blocks staggered with Bounce.Out, looping via yoyo + repeat: -1.

Staggered bouncing blocks Phaser 4 · sandboxed

this.tweens.stagger(step, options) returns a function that computes the delay for each target — 0, step, 2*step, … It’s the cleanest way to spread an effect across a group.

Chaining

For “do A, then B, then C,” use chain:

this.tweens.chain({
	targets: sprite,
	tweens: [
		{ x: 400, duration: 500, ease: 'Cubic.Out' },
		{ y: 200, duration: 300 },
		{ angle: 360, duration: 800, ease: 'Sine.InOut' },
	],
});

chain is the simpler successor to v3’s timeline. Each child entry is a normal tween config — easing, callbacks, yoyo, the lot.

Value tweens

When you need to interpolate something Phaser can’t reach via property assignment — a shader uniform, a CSS variable, a value in a third-party library — tween a plain object and read it in onUpdate:

const state = { mix: 0 };

this.tweens.add({
	targets: state,
	mix: 1,
	duration: 2000,
	ease: 'Cubic.InOut',
	onUpdate: () => myShader.setUniform('uMix', state.mix),
});

This pattern works for anything: easing a sound’s volume, animating a RenderTexture blend, fading a DOM overlay.

Stopping and managing tweens

const t = this.tweens.add({ targets: sprite, x: 400, duration: 1000 });

t.pause();
t.resume();
t.stop();                 // stop and destroy
t.seek(500);              // jump to 500ms in
t.timeScale = 2;          // play at 2× speed

this.tweens.killTweensOf(sprite);    // stop every tween touching this object
this.tweens.killAll();               // nuclear option

For “is this object currently tweening?” use this.tweens.isTweening(sprite).

Common pitfalls

Don’t tween position directly on physics objects. Tweens write .x/.y, bypassing the physics body. The body desyncs until the next physics step. Either tween body.velocity.* instead, or disable the body for the duration of the tween.

Garbage tweens. Tweens stay registered after they complete unless you tell them to clean up. For one-shot effects fired in a hot loop, use onComplete: () => tween.remove() — or use this.tweens.add({ ..., persist: false }) so the manager drops it automatically.

Scoped to the scene. Tweens live on this.tweens, the scene’s tween manager. They stop when the scene stops. Cross-scene tweens don’t exist — model the shared state instead.

Easing math is per-property. A tween with x and alpha will use the same ease for both unless you override per-property. If you want different curves, split into two tweens or use the per-property object form.

  • Animation — frame-based animation on sprites; complementary to tweens (which interpolate properties).
  • The Game Loop — tweens advance every frame using delta, so they’re framerate-independent for free.
  • Cameras — camera effects (shake, fade, pan, zoomTo) are tweens under the hood.