npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

com.kyrylokuzyk.primetween

v1.5.2

Published

PrimeTween is a high-performance, allocation-free animation library for Unity. Animate anything with just one line of code, tweak all animation properties directly from the Inspector, and create complex animation sequences. No runtime memory allocations,

Readme

PrimeTween

PrimeTween is a high-performance, allocation-free animation library for Unity. Animate anything with just one line of code, tweak all animation properties directly from the Inspector, and create complex animation sequences. No runtime memory allocations, ever.

Get PrimeTween PRO to animate anything without code.

Performance comparison with other tween libraries.

Asset Store | Forum | FAQ | YouTube

Table of Contents

Getting started

Installation

Import PrimeTween from the Asset Store. This is the preferred option because it comes with a Demo scene.
Optional: install via Unity Package Manager (UPM).

Animations

Without further ado, let's jump straight to the code!

using PrimeTween;

// Animate 'transform.position.y' from the current value to 10 in 1 second using the Ease.InOutSine
Tween.PositionY(transform, endValue: 10, duration: 1, ease: Ease.InOutSine);

// Rotate 'transform' from the current rotation to (0, 90, 0) in 1 second
Tween.Rotation(transform, endValue: Quaternion.Euler(0, 90, 0), duration: 1);

// Rotate 'transform' around the y-axis by 360 degrees in 1 second 
Tween.EulerAngles(transform, startValue: Vector3.zero, endValue: new Vector3(0, 360), duration: 1);

That's it!

Simply type Tween. and let your IDE suggest all supported animations. Out of the box, PrimeTween can animate almost everything: UI, UI Toolkit, Materials, Camera properties, Transforms, AudioSource, and whatnot.

Didn't find what you're looking for? No problem, use Tween.Custom() to animate anything.

To view PrimeTween's XML documentation in your IDE, enable this setting: 'Unity Preferences/External Tools/Generate .csproj files for/Local tarball' and press the 'Regenerate project files' button.

Shakes

// Shake the camera with medium strength (0.5f) 
Tween.ShakeCamera(camera, strengthFactor: 0.5f);

// Shake the camera with heavy strength (1.0f) for a duration of 0.5f seconds and a frequency of 10 shakes per second 
Tween.ShakeCamera(camera, strengthFactor: 1.0f, duration: 0.5f, frequency: 10);

// Shake the y-axis position with an amplitude of 1 unit 
Tween.ShakeLocalPosition(transform, strength: new Vector3(0, 1), duration: 1, frequency: 10);

// Shake the z-axis rotation with an amplitude of 15 degrees
Tween.ShakeLocalRotation(transform, strength: new Vector3(0, 0, 15), duration: 1, frequency: 10);

// Punch localPosition in the direction of 'punchDir'
var punchDir = transform.up;
Tween.PunchLocalPosition(transform, strength: punchDir, duration: 0.5f, frequency: 10);

Callbacks

Use .OnComplete() to execute custom code when the tween completes.

// Call SomeMethod() when the animation completes
Tween.Position(transform, endValue: new Vector3(10, 0), duration: 1)
    .OnComplete(() => SomeMethod());
    
// After the animation completes, wait for 0.5 seconds, then destroy the GameObject
Tween.Scale(transform, endValue: 0, duration: 1, endDelay: 0.5f)
    .OnComplete(() => Destroy(gameObject));

"But wait! There is a memory allocation in the example above," you might say. And you would be right: calling SomeMethod() or Destroy() captures this reference in a closure and allocates heap memory. See how to address this in the zero allocations section.

Delays

Creating delays is by far the most useful feature in game development. Delays in PrimeTween behave like normal tweens and can be used with sequences, coroutines, and async/await methods. All while being completely allocation-free.

Tween.Delay(duration: 1f, () => Debug.Log("Delay completed"));

Cycles

Animations can be repeated with the help of cycles. To apply cycles to an animation, pass the int cycles and CycleMode cycleMode parameters to a Tween. method. Setting cycles to -1 will repeat the tween indefinitely.

Tween.PositionY(transform, endValue: 10, duration: 0.5f, cycles: 2, cycleMode: CycleMode.Yoyo);

To apply cycles to a Sequence, use the Sequence.Create(cycles: numCycles, cycleMode: CycleMode.Yoyo) method.

Sequence.Create(cycles: 2, CycleMode.Yoyo)
    .Chain(Tween.PositionX(transform, 10, duration))
    .Chain(Tween.PositionY(transform, 20, duration));

enum CycleMode

  • Restart (default): restarts the tween from the beginning.
  • Yoyo: animates forth and back, like a yoyo. Easing is the same on the backward cycle.
  • Incremental: at the end of a cycle increments the endValue by the difference between startValue and endValue. For example, if a tween moves position.x from 0 to 1, then after the first cycle, the tween will move the position.x from 1 to 2, and so on.
  • Rewind: rewinds the tween as if time was reversed. Easing is reversed on the backward cycle.

SetRemainingCycles(int cycles)

Sets the number of remaining cycles of an animation. This method modifies the cyclesTotal so that the animation will complete after the number of cycles.

SetRemainingCycles(bool stopAtEndValue)

Stops an animation when it reaches 'startValue' or 'endValue' the next time.
For example, if you have an infinite animation (cycles == -1) with CycleMode.Yoyo/Rewind, and you wish to stop it when it reaches the 'endValue', then set stopAtEndValue to true. To stop the animation at the 'startValue', set stopAtEndValue to false.

Sequencing tweens

Sequence

There are several sequencing methods in PrimeTween. Let's start with the most common one: grouping tweens in Sequences.

Sequence is a group of tweens, callbacks, and other sequences. Animations in a sequence can overlap, run sequentially or in parallel, or any combination of those. You can control a Sequence the same way as individual tweens, see the controlling tweens section.

Sequence.Create(cycles: 10, CycleMode.Yoyo)
    // PositionX and Scale tweens are 'grouped', so they will run in parallel
    .Group(Tween.PositionX(transform, endValue: 10f, duration: 1.5f))
    .Group(Tween.Scale(transform, endValue: 2f, duration: 0.5f, startDelay: 1))
    // Rotation tween is 'chained' so it will start when both previous tweens are finished (after 1.5 seconds)
    .Chain(Tween.Rotation(transform, endValue: new Vector3(0f, 0f, 45f), duration: 1f)) 
    .ChainDelay(1)
    .ChainCallback(() => Debug.Log("Sequence cycle completed"))
    // Insert color animation at time of '0.5' seconds
    // Inserted animations overlap with other animations in the sequence
    .Insert(atTime: 0.5f, Tween.Color(image, Color.red, duration: 0.5f));

Insert(float atTime, Tween/Sequence animation)

Places animation inside this sequence at the specified time, overlapping with other animations. The total sequence duration is increased if the inserted animation doesn't fit inside the current sequence duration.

Chain(Tween/Sequence animation)

Places animation after all previously added animations in this sequence. Chained animations run sequentially after one another.

Group(Tween/Sequence animation)

Groups animation with the 'previous' animation in this Sequence. The 'previous' animation is the animation used in the preceding Group/Chain/Insert() method call. Grouped animations start at the same time and run in parallel.

Coroutines

Another sequencing method is waiting for animations in coroutines by calling .ToYieldInstruction().

IEnumerator Coroutine() {
    Tween.PositionX(transform, endValue: 10f, duration: 1.5f);
    yield return Tween.Scale(transform, 2f, 0.5f, startDelay: 1).ToYieldInstruction();
    yield return Tween.Rotation(transform, new Vector3(0f, 0f, 45f), 1f).ToYieldInstruction();
    // Non-allocating alternative to 'yield return new WaitForSeconds(1f)'
    yield return Tween.Delay(1).ToYieldInstruction(); 
    Debug.Log("Sequence completed");
}

Async/await

And the last method is awaiting animations using the async/await pattern. Async/await is a great tool to prevent callback hell in your code. PrimeTween doesn't use threads, so tweens can be awaited on all platforms, even on WebGL.

async void AsyncMethod() {
    Tween.PositionX(transform, endValue: 10f, duration: 1.5f);
    await Tween.Scale(transform, endValue: 2f, duration: 0.5f, startDelay: 1);
    await Tween.Rotation(transform, endValue: new Vector3(0f, 0f, 45f), duration: 1f);
    // Non-allocating alternative to 'await Task.Delay(1000)' that doesn't use 'System.Threading'
    // Animations can be awaited on all platforms, even on WebGL
    await Tween.Delay(1); 
    Debug.Log("Sequence completed");
}

While PrimeTween never allocates memory at runtime, the async/await feature in C# is allocating: awaiting an async method allocates a small amount of garbage. Likewise, StartCoroutine() also allocates garbage.
For performance-intensive code paths, use Sequence instead of async methods or Coroutines.

SetCancellationToken(CancellationToken cancellationToken, TweenSettings.CancellationMode? cancellationMode = null)

Use .SetCancellationToken() to stop an animation when a CancellationToken is canceled. By default, cancellation stops the animation as if Stop() was called. Cancellation happens on the next PrimeTween update; if the token is already canceled, the animation is canceled immediately. If the animation is awaited with 'await', the 'await' throws an OperationCanceledException when the token is canceled.

Pass cancellationMode: TweenSettings.CancellationMode.Complete to complete the animation on cancellation instead, as if Complete() was called: the animation jumps to its end value and OnComplete is called. To change the default for all animations, set PrimeTweenConfig.defaultCancellationMode.

To make a Sequence cancellable, call SetCancellationToken() on the Sequence itself instead of its child animations.

var cancellationTokenSource = new CancellationTokenSource();
Sequence.Create()
    .Chain(Tween.PositionX(transform, endValue: 10f, duration: 1.5f))
    .Chain(Tween.Scale(transform, endValue: 2f, duration: 0.5f))
    .SetCancellationToken(cancellationTokenSource.Token);

// ...
cancellationTokenSource.Cancel(); // stops the animation

Controlling tweens

All static Tween. methods return a Tween struct. While the tween.isAlive you can control it and access its properties such as duration, elapsedTime, progress, interpolationFactor, etc.

After completion, the tween becomes 'dead' and can't be reused. This ensures that completed tweens don't eat computing resources and prevents the common performance pitfalls encountered in other tween libraries.

Tween tween = Tween.LocalPositionX(transform, endValue: 1.5f, duration: 1f);
// ...
 
if (tween.isAlive) {
    // '.isAlive' means the tween was created and not completed (or manually stopped) yet.
    // While the tween '.isAlive' you can access its properties such as duration,
    //     elapsedTime, progress, interpolationFactor, etc.
    Debug.Log($"Animation is still running, elapsed time: {tween.elapsedTime}.");
}

tween.Stop(); // Interrupt the tween, leaving the animated value at the current value
Tween.StopAll(onTarget: transform); // Alternative way to stop the tween by its target

tween.Complete(); // Instantly complete the running tween and set the animated value to the endValue
Tween.CompleteAll(onTarget: transform); // Alternative way to complete the tween by its target

tween.isPaused = true; // Pause the tween
Tween.SetPausedAll(true, onTarget: transform); // Alternative way to pause the tween by its target

tween.elapsedTime = 0.5f; // Manually set the elapsed time of the tween. Use 'elapsedTimeTotal' to set the elapsed time of all cycles.

tween.progress = 0.5f; // Manually set the normalized progress of the current cycle. This property is similar to the 'normalizedTime' property of the Animation component.

tween.timeScale = 2f; // Apply a custom timeScale to the tween

As you can see, there is no way to change the direction of the currently running tween; it can only be stopped and completed. But how to play an animation forward and backward, for example, to show or hide a window? Easy! Just start a new Tween in the desired direction.

[SerializeField] RectTransform window;

public void SetWindowOpened(bool isOpened) {
    Tween.UIAnchoredPositionY(window, endValue: isOpened ? 0 : -500, duration: 0.5f);
}

In this example, the SetWindowOpened() can be called again while the previous animation is still running. Generally, there is no need to stop the previously running tween in such cases. The new tween will seamlessly start from the current position and overwrite all previously running tweens on the window. Several duplicated tweens are fine, but if your code can potentially create duplicated tweens every frame, then consider stopping the previous tween with the help of tween.Stop() or Tween.StopAll(onTarget: target).

Inspector integration

Inspector integration is the cornerstone of PrimeTween's design. It lets you tweak all animation properties from the Inspector without changing the code. It saves time and gives creative freedom. All animation settings can be serialized in MonoBehaviour or ScriptableObject and passed to the corresponding Tween methods.

// Tweak all animation properties from the Inspector:
//     startValue, endValue, duration, ease (or custom ease curve), etc.
[SerializeField] TweenSettings<float> yPositionTweenSettings;
// Then pass tween settings to the animation method
Tween.PositionY(transform, yPositionTweenSettings);

[SerializeField] TweenSettings<Vector3> rotationTweenSettings;
Tween.Rotation(transform, rotationTweenSettings);

[SerializeField] TweenSettings<Vector3> eulerAnglesTweenSettings;
Tween.EulerAngles(transform, eulerAnglesTweenSettings);

[SerializeField] ShakeSettings cameraShakeSettings;
Tween.ShakeLocalPosition(Camera.main.transform, cameraShakeSettings);

The neat thing about setting up animation properties in the Inspector is that you can at any time switch to a custom animation curve without touching the code.

Now let's revisit the window example from the Controlling tweens section and improve it by removing all magic numbers from the code. Notice how the isOpened parameter is passed to the WithDirection(bool toEndValue) method. This helper method selects the target position based on the isOpened parameter. Nice and simple!

[SerializeField] RectTransform window;

public void SetWindowOpened(bool isOpened) {
    Tween.UIAnchoredPositionY(window, endValue: isOpened ? 0 : -500, duration: 0.5f);
}

Window example with Inspector Integration:

[SerializeField] RectTransform window;
[SerializeField] TweenSettings<float> windowAnimationSettings;

public void SetWindowOpened(bool isOpened) {
    Tween.UIAnchoredPositionY(window, windowAnimationSettings.WithDirection(toEndValue: isOpened));
}

The generic TweenSettings can be serialized and tweaked from the Inspector in Unity 2020.1+. If you're using an older version of Unity, use the non-generic TweenSettings and serialize startValue/endValue separately.

Custom tweens

Use Tween.Custom() to animate literally anything. The supported types for custom tweens are float, Color, Vector2/3/4, Quaternion, and Rect.

float floatField;
Color colorField;

// Animate 'floatField' from 0 to 10 in 1 second
Tween.Custom(0, 10, duration: 1, onValueChange: newVal => floatField = newVal);

// Animate 'colorField' from white to black in 1 second
Tween.Custom(Color.white, Color.black, duration: 1, onValueChange: newVal => colorField = newVal);

As you may expect, custom tweens work with inspector integration the same way as regular tweens do.

[SerializeField] TweenSettings<float> tweenSettings;
float floatField;

Tween.Custom(tweenSettings, onValueChange: newVal => floatField = newVal);    

Advanced

Timescale

You can apply a custom timeScale to each individual animation with the help of the timeScale property.

To smoothly animate the timeScale, use the Tween.TweenTimeScale(Tween/Sequence tween, ...) method.
To animate the global Unity's Time.timeScale, use the Tween.GlobalTimeScale(...) method.

Clock (experimental)

Tween.Clock controls the time of multiple animations at once. This feature requires the PRIME_TWEEN_EXPERIMENTAL define in 'Scripting Define Symbols'.

var clock = new Tween.Clock();
Tween.PositionX(transform, endValue: 10f, duration: 1f).SetClock(clock);
Sequence.Create().Chain(Tween.Scale(transform, 2f, 1f)).SetClock(clock);
clock.timeScale = 0.5f; // slows down both animations

A manual clock advances its animations only by the time passed to clock.Tick(). Animations don't update in frames without a Tick():

var clock = new Tween.Clock { isManual = true };
void Start() {
    Tween.PositionX(transform, endValue: 10f, duration: 1f).SetClock(clock);
}
void Update() {
    clock.Tick(Time.deltaTime); // PrimeTween applies the tick on its next update
}

A manual clock also works with PrimeTweenConfig.ManualUpdate(): call clock.Tick() before PrimeTweenConfig.ManualUpdate() to apply the tick in that update.

OnUpdate

Use the tween.OnUpdate() callback to execute a custom callback when the animated value is updated.

// Rotate the transform around the y-axis as the animation progresses
Tween.PositionY(transform, endValue, duration)
    .OnUpdate(target: transform, (target, tween) => target.rotation = Quaternion.Euler(0, tween.interpolationFactor * 90f, 0));

// Call the OnPositionUpdated() method on every position change
Tween.PositionY(transform, endValue, duration)
    .OnUpdate(target: this, (target, tween) => target.OnPositionUpdated(tween.progress));

Speed-based animations

Tween._AtSpeed(transform, endValue, speed, ...) methods allow to create animations based on the speed instead of duration. When the speed-based tween is created, PrimeTween will calculate the duration with the help of a simple formula: duration = distance(startValue, endValue) / speed. Because the duration is calculated immediately, to chain speed-based tweens to one another, you have to specify the startValue for all subsequent tweens:

Tween.LocalPositionAtSpeed(transform, endValue: midPos, speed)
    // Set 'startValue: midPos' to continue the movement from the 'midPos' instead of the initial 'transform.position'
    .Chain(Tween.LocalPositionAtSpeed(transform, startValue: midPos, endValue: endPos, speed));

To animate a custom value based on the speed, pass useDurationAsSpeed: true to Tween.Custom() or Tween.CustomAdditive(). The distance between colors is measured in RGBA space, and the distance between quaternions is the angle in degrees:

Tween.Custom(material, new TweenSettings<Color>(Color.black, Color.white, speed), (target, color) => target.color = color, useDurationAsSpeed: true);

Custom easing

Animation curves

It's possible to pass AnimationCurve instead of Ease enum to tweening methods:

AnimationCurve animationCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
Tween.PositionY(transform, endValue, duration, animationCurve);

Parametric easing

Parametric easing gives the ability to customize the standard ease types. For example:

// Regardless of the current position and endValue, the bounce will have the exact amplitude of 1 meter 
Tween.PositionY(transform, endValue, duration, Easing.BounceExact(1));

Available parametric eases:

  • Easing.Overshoot(float strength): customizes the overshoot strength of Ease.OutBack.
  • Easing.Bounce(float strength): customizes the bounce strength of Ease.OutBounce.
  • Easing.BounceExact(float amplitude): customizes the exact amplitude of the first bounce in meters/angles.
  • Easing.Elastic(float strength, float period = 0.3f): customizes the strength and oscillation period of Ease.OutElastic.

LateUpdate/FixedUpdate

Use updateType parameter to choose which Unity event function will update the animation. The available options are Update, LateUpdate, and FixedUpdate.

// Use TweenSettings or TweenSettings<T> struct to pass the 'updateType' parameter to static 'Tween.' methods
Tween.PositionX(transform, endValue: 10f, new TweenSettings(duration: 1f, updateType: UpdateType.LateUpdate));

var tweenSettingsFloat = new TweenSettings<float>(endValue: 10f, duration: 1f, updateType: UpdateType.FixedUpdate);
Tween.PositionX(transform, tweenSettingsFloat);

// To update the Sequence in FixedUpdate(), pass the 'updateType' parameter to Sequence.Create()  
Sequence.Create(updateType: UpdateType.FixedUpdate);

Install via Unity Package Manager (UPM)

The Package Manager installation method allows to include PrimeTween in derivative (free and commercial) projects (templates, libraries, GitHub repositories). For more info, see the license.
This installation method also keeps the project structure clean.

  • Open 'Edit / Project Settings / Package Manager'.
  • Add a new Scoped Registry with Name: npm URL: https://registry.npmjs.org Scope(s): com.kyrylokuzyk.
  • Go to 'Window / Package Manager / Packages / My Registries'.
  • Install the PrimeTween package.

Or modify the Packages/manifest.json file manually:

{
  "dependencies": {
    "com.kyrylokuzyk.primetween": "1.5.2",
    ...
  },
  "scopedRegistries": [
    {
      "name": "npm",
      "url": "https://registry.npmjs.org/",
      "scopes": [
        "com.kyrylokuzyk"
      ]
    }
  ]
}

Zero allocations with delegates

C# delegates are a powerful language feature essential for game development. It gives us the ability to receive callbacks and pass methods to other methods. But when delegates are used in hot code paths carelessly, they can create performance issues.

Let's review the code from earlier. If SomeMethod() is an instance method, then calling it from the callback will implicitly capture the this reference, allocating heap memory.

Tween.Position(transform, new Vector3(10, 0), duration: 1)
    .OnComplete(() => SomeMethod()); // delegate allocation!

Here is how to fix the above code to be non-allocating. Notice how the this reference is passed to the OnComplete method, then the target parameter is used instead of calling SomeMethod() directly.

Tween.Position(transform, new Vector3(10, 0), duration: 1)
    .OnComplete(target: this, target => target.SomeMethod()); // no allocation

That's it! The same non-allocating approach can be used in all places where PrimeTween uses delegates.

Tween.Delay(this, duration: 1f, target => target.SomeMethod());

Tween.Custom(this, 0, 10, duration: 1, (target, newVal) => target.floatField = newVal);

var shakeSettings = new ShakeSettings(frequency: 10, strength: Vector3.one, duration: 1);
Tween.ShakeCustom(this, startValue: vector3Field, shakeSettings, (target, val) => target.vector3Field = val);

Debugging tweens

To debug tweens, select the PrimeTweenManager object under the DontDestroyOnLoad foldout in the scene Hierarchy. Here you can inspect all currently running tweens and their properties.

If the tween's target is UnityEngine.Object, you can quickly show it in the Hierarchy by clicking on the Unity Target field. This is a good reason to supply the target even when it's optional, like in the case of Tween.Delay() and Tween.Custom().

Also, the Inspector shows the 'Max alive tweens' for the current session. Use this number to estimate the maximum number of tweens required for your game and pass it to the PrimeTweenConfig.SetTweensCapacity(int capacity) method at the launch of your game. This will ensure PrimeTween doesn't allocate any additional memory at runtime.

PrimeTween PRO

PrimeTween PRO extends the free PrimeTween library with powerful new features for visual animation authoring, faster runtime performance, and cleaner material workflows.

Tween Animation

TweenAnimation is a serializable class that allows creating complex animations in the Inspector without writing any code.

// Add TweenAnimation to your script, then set the animation up in the Inspector.
[SerializeField] TweenAnimation doorAnimation = new();

void Update() {
    if (Input.GetKeyDown(KeyCode.Space)) {
        // Play the animation in response to gameplay events.
        doorAnimation.Trigger();
        
        // Or set the state directly.
        doorAnimation.state = true;
    }
}

After adding [SerializeField] TweenAnimation doorAnimation to your script, you'll be able to tweak all animation properties from the Inspector without changing the code. Add any number of animations by pressing the '+' button in the Inspector. Chain, delay, or insert animations at a specific time.

Preview controls at the top of the Inspector window allow you to preview the animation in Edit Mode without entering Play Mode or recompiling the scripts.

Trigger()

Plays the animation. The resulting animation state depends on the animation type (simple, reversible, or infinite). If the animation is already playing, applies interruptionMode.
Simple and infinite animations: plays the animation from the beginning.
Reversible animations: changes the direction.

state { get; set; }

Gets or sets the logical state of the animation. Instead of keeping track of the animation state manually, you can store the state in the animation directly.
State means different things depending on the animation type. Changing state applies interruptionMode.
Simple animations: returns true if an animation is currently playing.
Infinite animations: returns true if an animation is currently playing and NOT being interrupted by interruptionMode.
Reversible animations: returns true if the animation is moving forward OR already at the end. Changing state changes animation direction.

animationType

Controls how the animation responds to state changes and Trigger().

  • Simple: plays the animation from the beginning. Triggering the animation while it's playing applies interruptionMode.
  • Reversible: use for toggle animations (e.g., open/close or show/hide). Set state = true to play forward and state = false to reverse — no need for a separate direction variable. Or use Trigger() to toggle the current state.
  • Infinite: plays the animation infinitely. cycles is always -1.
  • Overlapping: triggering the animation while it's playing plays a new animation on top of the playing one. The previous animation continues playing until its end. Use it with additive tweens to blend and accumulate their changes.

Tween Animation Component

A component that exposes TweenAnimation to the scene. Consider using it if you need to reference the animation from other scripts or animations in the scene.
It allows an animation to be referenced in a scene and played from other MonoBehaviours or UnityEvents. It can also play an animation in response to Unity messages like OnEnable and OnDisable.

Per-object material animations

Tween.MaterialPropertyBlock...(Renderer target, ...) can animate any Renderer's material property without modifying the original material or creating its copies. It's useful for creating fire-and-forget material animations without the need to copy materials and manually destroy them later.

PrimeTween doesn't reset the MaterialPropertyBlock after animation completion to preserve the animated property at its endValue. As a result, directly modifying the Material's property will have no effect since the MaterialPropertyBlock takes precedence. To modify the sharedMaterial property after animation completion, call renderer.SetPropertyBlock(null) (or renderer.SetPropertyBlock(null, materialIndex)) to clear the override.

Materials can also be animated from the Inspector with TweenAnimation or TweenAnimationComponent.

Text Animator

TextAnimator brings text to life with per-character effects and typewriter animations. All animations are written directly in the text with rich text tags, so dialogues can be animated without writing a single line of code. TextAnimator works with TextMeshPro (Unity 2021.2+) and UI Toolkit (Unity 6000.6+, PanelRenderer). And just like the rest of PrimeTween, it doesn't allocate memory at runtime.

Add the TextAnimator component to a GameObject with a TextMeshPro or PanelRenderer component, then wrap any part of the text with an effect tag. By default, when the text changes, the typewriter reveals it character by character, and the effects start playing automatically.

[SerializeField] TextAnimator textAnimator;

textAnimator.SetText("Welcome, <gold>traveler</gold>!\n" +
                     "The <fire>dragon</fire> is <shake>furious</shake>.\n" +
                     "<wave>Run to the <rainb>rainbow bridge</rainb>!</wave>");

Instant preview in the Editor

TextAnimator allows instantly previewing the effects in Edit Mode without entering Play Mode. Just type the text into the TextMeshPro component or UI Toolkit's UI Builder and preview the result immediately!

Tweaking effect presets in TextAnimatorSettings applies the changes automatically for faster iteration.

Effects

Wrap the text with <effectName>Text with effect</effectName> to apply the effect preset to a particular section of the text. Effects can be combined by adding multiple nested tags. For example, <wave><rainb>rainbow bridge</> combines two effects.

All effect presets are stored in the TextAnimatorSettings → effectPresets, where you can tweak the existing presets or create your own.

Effect parameters

Effects can be adjusted right in the tag. Use = to set a value, or * to multiply the preset's value:

<wave a=0.3>    'a' is amplitude: sets the wave's amplitude to 0.3
<shake a*2>     shakes twice as strong as the preset
                'a' applies to offset, scale, rotation, and shake effects

<wave s*3>      's' is speed: the wave plays 3 times faster
<wave i=0.2>    'i' is phase between characters: each following character lags behind by 20% of the effect's duration
<shake a*2 s*3> multiple parameters can be combined in one tag
</>             closes all open effect tags

Controlling effects from code

By default, effects play automatically when the text changes. Disable autoPlayEffects to play them manually.

textAnimator.EffectSetPlaying(true); // play all effects with the default TextAnimatorSettings.effectFadeInDuration
textAnimator.EffectSetPlaying(true, "shake", fadeDuration: 0f); // instantly play only the '<shake>' effects without the fade-in
textAnimator.EffectSetPlaying(false); // fade out all effects with the default TextAnimatorSettings.effectFadeOutDuration

// toggle one specific effect tag from textAnimator.parsedTags
bool isEffectPlaying = textAnimator.EffectIsPlaying(effectTag);
textAnimator.EffectSetPlaying(!isEffectPlaying, effectTag);

EffectCreate(RangeInt range, EffectSettings effectSettings)

Animates a range of characters from code without a predefined effect preset. The method returns a regular Tween, so the animation can be added to a Sequence, awaited, or stopped like any other tween.

// Make the character at 'charIndex' jump once
Tween jump = textAnimator.EffectCreate(new RangeInt(charIndex, 1), new TextAnimator.EffectSettings(TextAnimator.TweenType.Offset) {
    endValue = new TweenAnimation.ValueWrapper { vector2 = new Vector2(0f, 0.5f) },
    duration = 0.15f,
    ease = Ease.OutQuad,
    cycles = 2
});

SetCharsVisible(bool isVisible, RangeInt? range = null)

Instantly shows or hides the whole text or a range of characters without modifying the text itself. Hidden characters still occupy their place in the text layout.

textAnimator.SetCharsVisible(false); // hide the whole text
textAnimator.SetCharsVisible(true, new RangeInt(0, 5)); // show the first 5 characters

Typewriter

When autoPlayTypewriter is enabled (default), the typewriter reveals the text every time it changes. To play the typewriter manually, call TypewriterPlay(). It returns a Sequence, so the typewriter can be awaited, used in coroutines, or completed to reveal the whole text instantly.

async void ShowDialogue(string text) {
    textAnimator.SetText(text);
    await textAnimator.TypewriterPlay();
    Debug.Log("The typewriter has finished.");
}

// Skip the typewriter and reveal the whole text instantly
textAnimator.typewriterAnimation.Complete();

// Hide the text character by character
textAnimator.TypewriterPlay(TextAnimator.TypewriterDirection.Hide);

// Hide the text starting from the last character
textAnimator.TypewriterPlay(TextAnimator.TypewriterDirection.HideFromEnd);

The typewriter supports different kinds of pauses: between characters, punctuation marks, words, sentences, etc. All typewriter settings can be configured in TextAnimatorSettings.

List of supported typewriter tags:

<speed=2>               sets the typewriter speed to 2
<speed*0.5>             multiplies the current speed by 0.5
<waitfor=1.5>           pauses the typewriter for 1.5 seconds
<waitfor>               pauses the typewriter for 1 second
<waitinput>             waits until the player presses any key or button
<waitinput timeout=3>   waits for any input, but no longer than 3 seconds
<waitinput=submit>      waits for an InputActionReference named 'submit' defined in TextAnimatorSettings.waitInputActions (Input System only)
{pop}text{/pop}         overrides the TextAnimatorSettings.defaultTypewriterShowPreset of the wrapped characters with the 'pop' preset
{#up}text{/#up}         overrides the TextAnimatorSettings.defaultTypewriterHidePreset of the wrapped characters with the 'up' preset
{/}                     closes all typewriter show preset tags
{/#}                    closes all typewriter hide preset tags
<?event>                custom typewriter event, see TypewriterGetNextTag(out RichTextTag textTag)
<?event s=Hello f=1.5>  custom typewriter event with any number of custom parameters

<speed>, <waitfor>, and <waitinput> only apply when the typewriter shows the text.

Typewriter events

Event tags like <?eventName> let you run custom code when the typewriter reaches a specific place in the text: shake the camera, play a sound, etc. Process the events in Update() with TypewriterGetNextTag(). Events support any number of custom parameters.

// textAnimator.SetText("Watch out!<?shakeCamera strength=0.5> The bridge is collapsing!");

void Update() {
    while (textAnimator.TypewriterGetNextTag(out var textTag)) {
        if (textTag.type == TextAnimator.RichTextTag.Type.Event && textTag.name.SequenceEqual("shakeCamera")) {
            foreach (var parameter in textTag.parameters) {
                if (parameter.name.SequenceEqual("strength") && parameter.floatValue is float strength) {
                    Tween.ShakeCamera(Camera.main, strength);
                    break;
                }
            }
        }
    }
}

Use TypewriterGetCurrentCount() to know how many characters the typewriter has revealed. For example, to play a typing sound:

int prevCharCount;

void Update() {
    int charCount = textAnimator.TypewriterGetCurrentCount();
    if (charCount != prevCharCount) {
        prevCharCount = charCount;
        typingSound.Play();
    }
}

TextAnimatorSettings

Effect and typewriter presets are stored in the TextAnimatorSettings prefab, which is created at Assets/Plugins/PrimeTween/TextAnimatorSettings.prefab when a TextAnimator is added to a scene for the first time. Customize the prefab directly, or create a prefab variant with 'Assets/Create/Prefab Variant' to give some texts a different style. A preset can contain multiple effects that combine and play simultaneously.

Adding a new effect or typewriter preset is as simple as pressing the "+" button in the Inspector: no need to write custom scripts or recompile any code.

  • phaseBetweenChars: delays the animation of each following character, producing a wave that travels along the text.
  • randomPhaseBetweenChars, phaseBetweenCharsRange: picks the delay between every two neighboring characters randomly from the from..to range, producing an irregular wave.
  • charVertexPhaseHorizontal/Vertical: delays the animation between the vertices of a character, so the character deforms instead of moving as a whole. Color animations get a per-character gradient.
  • perVertexStrength: the strength of the effect for each of the four character vertices, for example, to create a shearing effect.
  • pivot, useQuadPivot: the pivot to scale and rotate characters around.

Migrating from DOTween to PrimeTween

PrimeTween and DOTween don't conflict with each other and can be used in one project. You can check out all PrimeTween's performance benefits in your current DOTween project without breaking anything.

What are the reasons to try PrimeTween?

  • See performance comparison.
  • Seamless installation that never produces compilation errors regardless of what other packages or plugins your project already uses.
  • PrimeTween is extremely easy to learn. It has only 8 top-level concepts, and you can learn the API even without the documentation by simply typing PrimeTween and pressing .
  • PrimeTween is extensively covered by more than 250 automatic tests that ensure that every bit works perfectly.
  • With PrimeTween it's safe to destroy objects with running animations.
  • PrimeTween is truly multiplatform with no exceptions. Awaiting tweens in async methods works even on WebGL!

Performance comparison

Please visit the full performance comparison article.

DOTween adapter

PrimeTween comes with a built-in migration adapter that can help you migrate even big projects relatively quickly. The adapter can also be used if you're missing extension methods you've gotten used to.

The Adapter is an optional feature designed to speed up PrimeTween's adoption. The migrated code may still be allocating because of the delegate allocations.

Please back up your project before proceeding. You should test the migrated code thoroughly before releasing it to production.

First, to enable the adapter, add the PRIME_TWEEN_DOTWEEN_ADAPTER define to the ProjectSettings/Player/Script Compilation and press Apply.

The migration process may vary from project to project. In many cases, simply replacing using DG.Tweening; with using PrimeTween; is enough to switch a script from DOTween to PrimeTween. See how easy it was to migrate the MotionDesignFES project with dozens of complex animations.

Open a script that uses DOTween, change using DG.Tweening; to using PrimeTween; and the adapter will handle the majority of cases automatically, preserving DOTween's syntax in your project. The most common places that require a manual change to the existing code base:

// using DG.Tweening;
using PrimeTween;

// if (tween != null) {
//     tween.Kill(complete: true);
//     tween = null;
// }
tween.Complete(); // null checking and setting tween to null is not needed

// tween/sequence.PlayForward/PlayBackwards/Rewind/Restart();
// In PrimeTween, tweens and sequences are non-reusable, so there is no direct equivalent.
// Instead, start a new animation in the desired direction (see the example below).
// Starting new animations in PrimeTween is extremely fast, so there is no need for caching.

Tween.PlayForward/PlayBackwards/Restart

PrimeTween offers a different approach to animating things forward and backward that is simpler and has greater performance. In PrimeTween, tweens and sequences are non-reusable, which means that it's not possible to cache tweens and reuse them later.

Let's consider the common DOTween usage pattern: creating a tween once, then calling PlayForward() and PlayBackwards() when needed.

public class DOTweenWindow : MonoBehaviour {
    // Disable auto-kill and store tween reference to reuse the tween later.
    // Disabling auto-kill wastes resources: even when the tween is not running, it still receives an update every frame and consumes memory.
    Tween tween;

    void Awake() {
        tween = transform.DOLocalMove(Vector3.zero, 1)
            .ChangeStartValue(new Vector3(0, -500))
            .SetEase(Ease.InOutSine)
            .SetAutoKill(false)
            // Option 1: link the tween to this GameObject, so the tween is killed when the GameObject is destroyed
            .SetLink(gameObject)
            // Paused tweens still receive updates every frame and consume resources
            .Pause();
    }

    public void SetWindowOpened(bool isOpened) {
        if (isOpened) {
            tween.PlayForward();
        } else {
            tween.PlayBackwards();
        }
    }
    
    void OnDestroy() {
        // Option 2: kill the tween before destroying an object.
        tween.Kill();
        
        // Option 3: enable 'Safe Mode' and don't use SetLink() and don't kill the tween in OnDestroy(). 
        // BUT:
        // - 'Safe Mode' will silence other potential errors or exceptions
        // - 'Safe Mode' doesn't work on WebGL and with 'Fast and no exceptions' on iOS
    }
}

PrimeTween offers a much more elegant way of doing the same that comes with much better performance. Destroying the window while the animation is playing is perfectly fine:

public class PrimeTweenWindow : MonoBehaviour {
    public void SetWindowOpened(bool isOpened) {
        Tween.LocalPosition(transform, isOpened ? Vector3.zero : new Vector3(0, -500), 1, Ease.InOutSine);
    }
}

Migration cheatsheet

If you don't want to use the Adapter and wish to migrate your project to PrimeTween's syntax, please refer to this cheatsheet for the API differences between the two libraries.

DOTween API on the left              -->  PrimeTween API on the right

// All animations are supported, here are only a few of them as an example
transform.DOMove(...)                -->  Tween.Position(transform, ...)
uiImage.DOFade(...)                  -->  Tween.Alpha(uiImage, ...)
material.DOColor(...)                -->  Tween.Color(material, ...)
transform.DOShakePosition(...)       -->  Tween.ShakeLocalPosition(transform, ...)
    
tween.SetEase(Ease.InOutSine)        --> Tween.Position(..., ease: Ease.InOutSine);
sequence.SetEase(Ease.OutBounce)     --> Sequence.Create(..., sequenceEase: Ease.OutBounce)

tween.SetLoops(2, LoopType.Yoyo)     --> Tween.Position(..., cycles: 2, CycleMode.Rewind) // Yoyo in DOTween works like Rewind
sequence.SetLoops(2, LoopType.Yoyo)  --> Sequence.Create(cycles: 2, Sequence.SequenceCycleMode.Rewind)
    
tween.SetUpdate(true)                --> Tween.Position(..., useUnscaledTime: true)
sequence.SetUpdate(true)             --> Sequence.Create(..., useUnscaledTime: true)

tween.SetUpdate(UpdateType.Fixed)    --> Tween.Position(..., new TweenSettings(1f, updateType: UpdateType.Fixed)) 
sequence.SetUpdate(UpdateType.Fixed) --> Sequence.Create(updateType: UpdateType.Fixed)
                                     --> github.com/KyryloKuzyk/PrimeTween#lateupdatefixedupdate

tween.Kill(false)                    --> tween.Stop()
tween.Kill(true)                     --> tween.Complete()

DOVirtual.DelayedCall()              --> Tween.Delay()
DOTween.To()                         --> Tween.Custom()
DOVirtual.Vector3()                  --> Tween.Custom()

DOTween.Sequence()                   --> Sequence.Create()
sequence.Join()                      --> sequence.Group()
sequence.Append()                    --> sequence.Chain() 
sequence.AppendCallback()            --> sequence.ChainCallback() 
seq.Insert(1.5f, trans.DOMoveX(...)) --> seq.Insert(1.5f, Tween.PositionX(trans, ...)) 
                                     --> // or `seq.Group(Tween.PositionX(..., startDelay: 1.5f))` before the first Chain() operation
seq.InsertCallback(1f, callback)     --> seq.InsertCallback(1f, callback)    

DOTween.Kill(target, false)          --> Tween.StopAll(onTarget: target)
DOTween.Kill(target, true)           --> Tween.CompleteAll(onTarget: target)

yield return tween.WaitForCompletion()    -->  yield return tween.ToYieldInstruction()
yield return sequence.WaitForCompletion() -->  yield return sequence.ToYieldInstruction()

// PrimeTween doesn't use threads, so you can write async methods even on WebGL
await tween.AsyncWaitForCompletion()      -->  await tween
await sequence.AsyncWaitForCompletion()   -->  await sequence

transform.DOMoveX(to, 1).From(from)       --> Tween.PositionX(transform, from, to, 1)
  
tween.SetDelay(1f).OnStart(callback)      --> Tween.Delay(1, callback).Chain(tween)
sequence.OnStart(callback)                --> sequence.ChainCallback(callback) // at the beginning of the sequence

trans.DOMove(pos, speed).SetSpeedBased()  --> Tween.PositionAtSpeed(trans, pos, speed)
DOTween.To(getter, setter, to, speed)     --> Tween.Custom(new TweenSettings<float>(to, speed), setter, getter, useDurationAsSpeed: true)
  .SetSpeedBased()

textMeshPro.DOText(...)         --> discussions.unity.com/t/926420/159 
                                --> or see TypewriterAnimatorExample.cs in Demo
                                --> or use textAnimator.TypewriterPlay(), see #text-animator
text.DOCounter()                --> discussions.unity.com/t/926420/80
transform.DOJump()              --> discussions.unity.com/t/926420/4
transform.DOPath()              --> discussions.unity.com/t/926420/158
transform.DOLookAt()            --> discussions.unity.com/t/926420/189
tween.SetId()                   --> github.com/KyryloKuzyk/PrimeTween/discussions/26#discussioncomment-7700985
target.DOBlendable___(...)      --> Tween.___Additive(target, ...)
                                --> github.com/KyryloKuzyk/PrimeTween/discussions/55

Support

Join the discussion on Unity Discussions.
Please submit bug reports here.
Submit your questions and feature requests here.
If you want to contact me privately, please drop me an email: [email protected]