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.
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.
Related
- 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.