Quick answer: Godot 4 removed the Tween node entirely. You must now use create_tween() to create Tween objects procedurally instead of adding a Tween node to the scene tree.

Here is how to fix Godot tween not working Godot 4. You upgraded your project to Godot 4, and now every Tween in your game is broken. The Tween node you carefully placed in the scene tree is gone from the class list, interpolate_property() throws errors, and your UI animations sit frozen on screen. This is one of the most common migration issues in Godot 4, and the fix requires rethinking how tweens work entirely.

The Symptom

After migrating a Godot 3.x project to Godot 4, you see errors like Invalid call. Nonexistent function 'interpolate_property' in base 'Tween' or Cannot find class "Tween" as a node type. If you had Tween nodes in your scene tree, they may have been silently removed during the conversion process. Scripts that reference $Tween fail with null access errors. Tweens that previously animated properties like modulate, rect_position, or rect_scale no longer run at all.

In some cases, the project converter renames references but leaves behind broken code that compiles without errors yet does nothing at runtime. You press play, and the fade-in that used to greet the player simply never happens. The button hover effect that scaled up? Static. The health bar that smoothly depleted? Jumps instantly to its target value.

What Causes This

Godot 4 completely replaced the Tween system. In Godot 3, Tween was a node you added to the scene tree, configured with interpolate_property() and started with start(). In Godot 4, the Tween node no longer exists. Instead, tweens are lightweight objects created procedurally with create_tween().

The old API looked like this:

# Godot 3 — the old way (no longer works)
var tween = $Tween
tween.interpolate_property(
  self, "modulate:a",
  0.0, 1.0, 0.5,
  Tween.TRANS_SINE, Tween.EASE_IN_OUT
)
tween.start()

Every part of this API changed. The node reference is gone because Tween is no longer a node. interpolate_property() was replaced by tween_property(). The transition and easing are now set via method chaining. And you do not call start() because tweens start automatically upon creation.

The property names also changed in Godot 4. What was rect_position is now just position for Control nodes. What was rect_scale is now scale. If your old tween targeted these renamed properties, the tween silently does nothing even if the syntax is correct.

The Fix

Step 1: Remove the Tween node from your scene. Open each scene that had a Tween child node and delete it. You no longer need it. If your script grabs a reference with $Tween or onready var tween = $Tween, remove those lines too.

Step 2: Create tweens procedurally with create_tween(). In any node script, you can call create_tween() to get a fresh Tween object. This tween is bound to the node that created it and will automatically be killed when that node exits the tree.

# Godot 4 — fade in a sprite
func fade_in():
  var tween = create_tween()
  tween.tween_property(self, "modulate:a", 1.0, 0.5)

Step 3: Use method chaining for transitions and easing. Instead of passing transition and easing types as arguments, chain set_trans() and set_ease() on the tween or on individual tweeners:

# Godot 4 — chained transition and easing
func animate_button_hover():
  var tween = create_tween()
  tween.set_trans(Tween.TRANS_SINE)
  tween.set_ease(Tween.EASE_IN_OUT)
  tween.tween_property(self, "scale", Vector2(1.1, 1.1), 0.2)

Step 4: Chain multiple animations. In Godot 4, tweeners run sequentially by default. To run them in parallel, call set_parallel() on the tween. You can also mix sequential and parallel steps:

# Sequential: move then fade out
func move_and_fade():
  var tween = create_tween()
  tween.tween_property(self, "position", Vector2(500, 300), 0.5)
  tween.tween_property(self, "modulate:a", 0.0, 0.3)

# Parallel: move and fade at the same time
func move_and_fade_parallel():
  var tween = create_tween().set_parallel()
  tween.tween_property(self, "position", Vector2(500, 300), 0.5)
  tween.tween_property(self, "modulate:a", 0.0, 0.3)

Step 5: Handle looping and re-creation. Godot 4 tweens are one-shot by default. For looping, use set_loops(). For tweens that need to restart — like a button hover that plays every time the mouse enters — kill the old tween and create a new one:

var hover_tween: Tween

func _on_mouse_entered():
  # Kill the previous tween if it exists
  if hover_tween and hover_tween.is_running():
    hover_tween.kill()
  hover_tween = create_tween()
  hover_tween.tween_property(self, "scale", Vector2(1.05, 1.05), 0.15)

func _on_mouse_exited():
  if hover_tween and hover_tween.is_running():
    hover_tween.kill()
  hover_tween = create_tween()
  hover_tween.tween_property(self, "scale", Vector2.ONE, 0.15)

Step 6: Use callbacks and intervals. The old interpolate_callback() and interpolate_deferred_callback() are replaced by tween_callback() and tween_interval():

# Run a callback after the tween finishes
func fade_and_free():
  var tween = create_tween()
  tween.tween_property(self, "modulate:a", 0.0, 0.3)
  tween.tween_callback(queue_free)

Why This Works

The new Tween system in Godot 4 was designed to be simpler and more performant. By removing the node requirement, tweens no longer occupy space in the scene tree and do not trigger the overhead associated with node lifecycle management. Creating them procedurally means you never have stale references to a Tween node that might have been freed.

The method chaining API reduces boilerplate significantly. What used to take five lines with explicit parameters now takes one or two lines. Tweens automatically start when created, so you cannot forget to call start(). And because they are bound to the creating node, they are automatically cleaned up when the node leaves the tree — no more orphaned tweens running in the background.

The kill-and-recreate pattern for restarting tweens may feel heavier than calling start() again, but it is actually more predictable. In Godot 3, restarting a running tween could produce unexpected intermediate states. In Godot 4, killing and recreating guarantees a clean slate every time.

Related Issues

If your AnimationPlayer tracks are also failing to update node properties after migration, see Fix: AnimationPlayer Property Track Not Updating Node Properties — property names changed across the board, not just in tweens.

For flickering sprite animations that might be competing with tweens for the same properties, check Fix: Sprite Animation Flickering Between Frames in Godot.

If your AnimationTree blend values are not transitioning smoothly and you suspect the tween-based approach you were using to interpolate blend positions is broken, see Fix: Godot AnimationTree Blend Values Not Transitioning Smoothly for the correct approach using set() with lerp in _process().

Tweens changed completely. The new way is better once you learn it.