Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when implementing tweens — property animation, method tweening, chaining, parallel sequences, easing, and common UI/gameplay motion recipes
.claude/skills/jame581-tween-animation/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-12 | ✗→✓ | ▲ Improved | 80% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 86% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 176% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 118% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 401% | 0% |
All examples target Godot 4.3+ with no deprecated APIs. GDScript is shown first, then C#.
> Related skills: animation-system for AnimationPlayer/AnimationTree (keyframe-based), godot-ui for UI transitions, shader-basics for tweening shader parameters, camera-system for camera shake and transitions, math-essentials for easing curves and interpolation math, particles-vfx for code-driven VFX timing and sequencing.
| Feature | Tween (code-driven) | AnimationPlayer (data-driven) | |-----------------|--------------------------------------------|--------------------------------------------| | Setup | Code only — no editor needed | Animation panel with keyframes | | Best for | Procedural motion, UI transitions, VFX | Complex multi-track, artist-driven clips | | Reusability | Recreated per use — lightweight | Saved as resources, reusable across scenes | | Blending | No — one tween per property at a time | Yes — AnimationTree supports blending | | Method calls | tween_callback() at any point | Call Method tracks at keyframed times |
Rule of thumb: Use Tweens for one-off procedural animations (fade in, bounce, slide). Use AnimationPlayer for repeating, artist-tuned animations (walk cycle, attack sequence).
Tweens are created from any Node and auto-bind to it. When the node is freed, the tween stops automatically.
gdscript# Creates a tween bound to this node var tween := create_tween() tween.tween_property(self, "position", Vector2(400, 300), 1.0)
csharpvar tween = CreateTween(); tween.TweenProperty(this, "position", new Vector2(400, 300), 1.0f);
> Important: Each call to create_tween() creates a new tween. Previous tweens on the same property are not automatically killed — they compete. Kill old tweens before creating new ones on the same property if you don't want conflicts.
gdscriptvar tween := create_tween() # Animate position over 0.5 seconds tween.tween_property($Sprite2D, "position", Vector2(200, 100), 0.5) # Animate modulate alpha (fade out) tween.tween_property($Sprite2D, "modulate:a", 0.0, 0.3)
csharpvar tween = CreateTween(); tween.TweenProperty(GetNode("Sprite2D"), "position", new Vector2(200, 100), 0.5); tween.TweenProperty(GetNode("Sprite2D"), "modulate:a", 0.0f, 0.3);
Sub-property access: Use : to target individual components — "position:x", "modulate:a", "scale:y".
gdscriptvar tween := create_tween() tween.tween_property(self, "position", Vector2.ZERO, 0.5) tween.tween_callback(func(): print("Arrived!")) tween.tween_callback(queue_free)
csharpvar tween = CreateTween(); tween.TweenProperty(this, "position", Vector2.Zero, 0.5f); tween.TweenCallback(Callable.From(() => GD.Print("Arrived!"))); tween.TweenCallback(Callable.From(QueueFree));
gdscriptvar tween := create_tween() tween.tween_property(self, "modulate:a", 0.0, 0.3) # fade out tween.tween_interval(1.0) # wait 1 second tween.tween_property(self, "modulate:a", 1.0, 0.3) # fade back in
csharpvar tween = CreateTween(); tween.TweenProperty(this, "modulate:a", 0.0f, 0.3); tween.TweenInterval(1.0f); tween.TweenProperty(this, "modulate:a", 1.0f, 0.3);
gdscript# Animate a method that receives interpolated float values func _ready() -> void: var tween := create_tween() tween.tween_method(_set_health_bar, 100.0, 0.0, 2.0) func _set_health_bar(value: float) -> void: $HealthBar.value = value $HealthLabel.text = "%d%%" % int(value)
csharppublic override void _Ready() { var tween = CreateTween(); tween.TweenMethod(Callable.From<float>(SetHealthBar), 100.0f, 0.0f, 2.0f); } private void SetHealthBar(float value) { GetNode<ProgressBar>("HealthBar").Value = value; GetNode<Label>("HealthLabel").Text = $"{(int)value}%"; }
By default, tweeners run sequentially — each waits for the previous to finish.
gdscriptvar tween := create_tween() tween.tween_property(self, "position:x", 300.0, 0.5) # Step 1 tween.tween_property(self, "position:y", 200.0, 0.5) # Step 2 (after Step 1) tween.tween_property(self, "rotation", PI, 0.3) # Step 3 (after Step 2)
set_parallel(true) — All tweeners run at oncegdscriptvar tween := create_tween().set_parallel(true) tween.tween_property(self, "position", Vector2(300, 200), 0.5) tween.tween_property(self, "rotation", PI, 0.5) tween.tween_property(self, "modulate:a", 0.5, 0.5)
csharpvar tween = CreateTween().SetParallel(true); tween.TweenProperty(this, "position", new Vector2(300, 200), 0.5f); tween.TweenProperty(this, "rotation", Mathf.Pi, 0.5f); tween.TweenProperty(this, "modulate:a", 0.5f, 0.5f);
chain() — Switch back to sequential mid-tweengdscriptvar tween := create_tween().set_parallel(true) # These two run at the same time tween.tween_property(self, "position", Vector2(300, 200), 0.5) tween.tween_property(self, "scale", Vector2(2, 2), 0.5) # Switch back to sequential for the next step tween.chain().tween_property(self, "modulate:a", 0.0, 0.3) tween.tween_callback(queue_free)
csharpvar tween = CreateTween().SetParallel(true); tween.TweenProperty(this, "position", new Vector2(300, 200), 0.5f); tween.TweenProperty(this, "scale", new Vector2(2, 2), 0.5f); tween.Chain().TweenProperty(this, "modulate:a", 0.0f, 0.3f); tween.TweenCallback(Callable.From(QueueFree));
parallel() — Make the next tweener parallel with the previousgdscriptvar tween := create_tween() tween.tween_property(self, "position", Vector2(300, 200), 0.5) # This runs at the same time as position, not after tween.parallel().tween_property(self, "rotation", PI, 0.5) # This runs after both finish (back to sequential) tween.tween_callback(func(): print("done"))
gdscriptvar tween := create_tween() # Set defaults for the entire tween tween.set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT) tween.tween_property(self, "position", Vector2(400, 300), 0.6)
csharpvar tween = CreateTween(); tween.SetTrans(Tween.TransitionType.Cubic).SetEase(Tween.EaseType.Out); tween.TweenProperty(this, "position", new Vector2(400, 300), 0.6f);
gdscriptvar tween := create_tween() # This tweener uses bounce, overriding the tween default tween.tween_property(self, "position:y", 0.0, 0.5) \ .set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT)
csharpvar tween = CreateTween(); tween.TweenProperty(this, "position:y", 0.0f, 0.5f) .SetTrans(Tween.TransitionType.Bounce) .SetEase(Tween.EaseType.Out);
| Transition | Character | Common Use | |------------------|----------------------------------------|-------------------------------------| | TRANS_LINEAR | Constant speed | Progress bars, timers | | TRANS_SINE | Gentle acceleration | Subtle UI animations | | TRANS_QUAD | Moderate curve | General movement | | TRANS_CUBIC | Smooth curve | Most UI transitions | | TRANS_QUART | Strong curve | Dramatic entrances | | TRANS_QUINT | Very strong curve | Emphasis effects | | TRANS_EXPO | Exponential — sharp start/stop | Snap-to-position | | TRANS_CIRC | Circular — smooth deceleration | Natural motion | | TRANS_BACK | Overshoots target slightly | Bouncy UI buttons | | TRANS_ELASTIC | Spring oscillation | Playful/cartoon effects | | TRANS_BOUNCE | Bounces at the end | Dropping objects, landing |
| Ease | Behavior | |---------------|-----------------------------------------| | EASE_IN | Slow start, fast end | | EASE_OUT | Fast start, slow end (most natural) | | EASE_IN_OUT | Slow at both ends | | EASE_OUT_IN | Fast at both ends (rarely used) |
> Most common combo: TRANS_CUBIC + EASE_OUT for natural-feeling UI transitions. TRANS_BACK + EASE_OUT for playful bounce-in effects.
Each tween_property() call returns a PropertyTweener you can chain modifiers on: .from(value) for a custom start, .from_current() to capture current value, .as_relative() to add (not replace), .set_delay(seconds) to delay this tweener's start.
> See references/property-tweener-modifiers.md for full code examples of each modifier.
tween.set_loops(N) repeats N times (0 = infinite). Signals: finished (whole tween done), step_finished(idx) (one step done), loop_finished(loop_count) (one full cycle done).
> See references/looping-and-signals.md for loop-count semantics and signal wiring.
Tweens are owned by their host SceneTree. Kill running tweens with tween.kill() before starting a new one to avoid stacking. Use set_pause_mode, set_speed_scale, set_ignore_time_scale for runtime control.
> See references/lifecycle.md for kill/replace patterns, pause modes, speed scale, ignore-time-scale.
Tween.has_tweeners() (const) returns true if any Tweener has been added to the tween and the tween is valid — useful when tweeners are appended dynamically and the tween can end up empty. Killing an empty tween before it starts prevents errors.
gdscriptvar tween := create_tween() _add_intro_steps(tween) # may append zero tweeners if not tween.has_tweeners(): tween.kill()
csharpvar tween = CreateTween(); AddIntroSteps(tween); // may append zero tweeners if (!tween.HasTweeners()) tween.Kill();
The patterns most projects need: fade in/out, UI panel slide in/out, button press bounce, damage number popup, pulsing/breathing effect, screen shake, shader parameter animation.
> See references/common-recipes.md for ready-to-use code for each recipe.
| Symptom | Cause | Fix | |-------------------------------------|---------------------------------------------------|--------------------------------------------------------------------| | Tween jumps or jitters | Multiple tweens competing on the same property | Kill the old tween before creating a new one | | Tween does nothing | Node was freed before tween finished | Check is_valid() or use tween_callback instead of await | | Tween runs during pause | Default pause mode doesn't match expectation | Set set_pause_mode(TWEEN_PAUSE_PROCESS) for pause-immune tweens | | from() value ignored | Called after set_parallel() changed ordering | Call from() directly on the PropertyTweener, not on the Tween | | Tween doesn't loop smoothly | End value doesn't match start for seamless loop | Use as_relative() for rotation, or match start/end values | | Relative tween drifts over loops | as_relative() accumulates each loop | Use absolute values for looping; relative for one-shot moves | | Callback fires at wrong time | Callback added to parallel section | Use chain() to switch back to sequential before the callback | | Tween property path not found | Typo or wrong path format | Use "property:component" — e.g., "position:x", "modulate:a" | | Tween faster/slower than expected | Engine.time_scale affects tween | Use set_ignore_time_scale() for UI tweens during slow-mo |
TRANS_CUBIC + EASE_OUT for natural motion, not default TRANS_LINEAR)set_parallel(true) is used when multiple properties should animate simultaneouslychain() is used to switch back to sequential after parallel sectionsfrom() or from_current() is used when the start value matters (not just the end value)as_relative() is used for incremental movement instead of computing absolute targetsset_loops() — not manual recreationset_pause_mode(TWEEN_PAUSE_PROCESS)tween_callback(queue_free) is used at the end of fire-and-forget sequences (damage numbers, particles)| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | pass→pass | 10,514 | 6,424 | -39% | 1 | 1 | 0% | 1,784 | 4,926 | +176% | 0 | 0 | — |
case-01 | pass→pass | 15,642 | 10,470 | -33% | 1 | 1 | 0% | 2,625 | 5,710 | +118% | 0 | 0 | — |
case-02 | pass→pass | 5,047 | 3,206 | -36% | 1 | 1 | 0% | 877 | 4,398 | +401% | 0 | 0 | — |
case-03 | pass→pass | 12,804 | 6,580 | -49% | 1 | 1 | 0% | 2,277 | 5,013 | +120% | 0 | 0 | — |
case-04 | pass→pass | 11,392 | 8,817 | -23% | 1 | 1 | 0% | 2,055 | 5,453 | +165% | 0 | 0 | — |
case-05 | pass→pass | 5,406 | 4,340 | -20% | 1 | 1 | 0% | 971 | 4,676 | +382% | 0 | 0 | — |
case-07 | pass→pass | 8,454 | 4,397 | -48% | 1 | 1 | 0% | 1,383 | 4,612 | +233% | 0 | 0 | — |
case-08 | pass→pass | 5,355 | 3,964 | -26% | 1 | 1 | 0% | 1,029 | 4,632 | +350% | 0 | 0 | — |
case-09 | pass→pass | 7,113 | 4,995 | -30% | 1 | 1 | 0% | 1,211 | 4,685 | +287% | 0 | 0 | — |
case-10 | pass→pass | 12,200 | 12,330 | +1% | 1 | 1 | 0% | 2,075 | 5,866 | +183% | 0 | 0 | — |
case-11 | pass→pass | 10,117 | 7,104 | -30% | 1 | 1 | 0% | 1,840 | 5,228 | +184% | 0 | 0 | — |
case-12 | fail→pass | 16,506 | 5,286 | -68% | 1 | 1 | 0% | 2,678 | 4,830 | +80% | 0 | 0 | — |
case-13 | fail→pass | 13,871 | 3,870 | -72% | 1 | 1 | 0% | 2,442 | 4,541 | +86% | 0 | 0 | — |
case-14 | pass→pass | 8,353 | 4,391 | -47% | 1 | 1 | 0% | 1,408 | 4,627 | +229% | 0 | 0 | — |
case-15 | pass→pass | 10,467 | 5,001 | -52% | 1 | 1 | 0% | 1,760 | 4,712 | +168% | 0 | 0 | — |
case-16 | pass→pass | 8,714 | 5,347 | -39% | 1 | 1 | 0% | 1,508 | 4,907 | +225% | 0 | 0 | — |
case-17 | pass→pass | 5,184 | 4,863 | -6% | 1 | 1 | 0% | 896 | 4,785 | +434% | 0 | 0 | — |
case-18 | pass→pass | 14,299 | 3,521 | -75% | 1 | 1 | 0% | 2,451 | 4,434 | +81% | 0 | 0 | — |
case-19 | pass→pass | 10,847 | 6,419 | -41% | 1 | 1 | 0% | 1,899 | 4,985 | +163% | 0 | 0 | — |
case-20 | pass→pass | 15,171 | 11,773 | -22% | 1 | 1 | 0% | 2,486 | 5,820 | +134% | 0 | 0 | — |
case-21 | fail→fail | 15,178 | 13,841 | -9% | 1 | 1 | 0% | 2,540 | 6,553 | +158% | 0 | 0 | — |
case-22 | pass→pass | 12,987 | 14,451 | +11% | 1 | 1 | 0% | 2,382 | 6,431 | +170% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +9 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.