Skip to main content

VRCTween

VRCTween allows you to create smooth animations in your VRChat worlds using the powerful DOTween library. You can animate positions, rotations, scales, and more with a few lines of code.

Overview​

VRCTween is a built-in tweening system that smoothly interpolates (or "tweens") values over time. Instead of manually updating positions or rotations frame-by-frame, VRCTween handles the math and timing for you.

Common use cases:

  • Animate UI elements such as buttons, panels, and menus.
  • Create smooth object movements.
  • Add polish to your world.

VRCTween uses DOTween, a popular Unity tweening library, and works with both UdonSharp and Udon Graph.

Basic Usage​

Create a Tween​

Create tweens using extension methods on the target type, or static methods on VRCTween. Each method returns a VRCTweenHandle that you can use to control and configure the tween:

using VRC.SDK3.Components;


public class MyScript : UdonSharpBehaviour
{
public GameObject cube;

void Start()
{
// Move the cube up over 2 seconds.
VRCTweenHandle tweenHandle = cube.TweenPosition(new Vector3(0, 5, 0), 2f, VRCTweenEase.OutQuad);
}
}

Receive Callbacks​

To receive a notification when a tween completes, chain .OnComplete() on the handle:

public class MyScript : UdonSharpBehaviour
{
void Start()
{
gameObject.TweenScale(Vector3.one * 2f, 1f, VRCTweenEase.OutBounce)
.OnComplete(this, nameof(OnScaleComplete));
}

public void OnScaleComplete()
{
Debug.Log("Scale animation complete!");
}
}

Tween Types​

Tween creation methods are available as extension methods on the target type (e.g., gameObject.TweenPosition(...)) or as static methods on VRCTween. All return a VRCTweenHandle.

  • Transform: TweenPosition, TweenLocalPosition, TweenRotation, TweenLocalRotation, TweenScale
  • Path: TweenPath, TweenLocalPath
  • UI: TweenColor, TweenFade (Graphic), TweenFade (CanvasGroup), TweenValue (Slider), TweenAnchorPos, TweenSizeDelta
  • Sprite: TweenColor, TweenFade (SpriteRenderer)
  • Renderer: TweenColor, TweenFloat (Renderer)
  • Light: TweenIntensity, TweenColor (Light)
  • Audio: TweenVolume, TweenPitch (AudioSource)

For detailed code examples, see Built-in Tween Types.

Virtual Tweens​

Virtual tweens animate arbitrary values (float, int, Color, Vector3) by writing into a variable on your UdonBehaviour each frame. Use them when none of the built-in tween types cover what you need. See Virtual Tweens for details and examples.

Controlling and Configuring Tweens​

Control and configure tweens using instance methods on VRCTweenHandle. Config methods return the handle for chaining:

cube.TweenPosition(new Vector3(0, 5, 0), 2f, VRCTweenEase.OutQuad)
.From()
.SetDelay(0.5f)
.SetLoops(2, VRCTweenLoopType.Yoyo)
.OnComplete(this, nameof(OnDone));

See Settings and Control for the full API reference and ease type table.

Example​

This example demonstrates handle storage, callbacks, and cleanup:

using UdonSharp;
using UnityEngine;
using VRC.SDK3.Components;


public class TweenExample : UdonSharpBehaviour
{
public GameObject button;
private VRCTweenHandle scaleTween;

public override void Interact()
{
// Scale up and down when clicked.
scaleTween = button.TweenScale(Vector3.one * 1.2f, 0.15f, VRCTweenEase.OutQuad)
.OnComplete(this, nameof(OnTweenComplete));
}

public void OnTweenComplete()
{
// Scale back down.
button.TweenScale(Vector3.one, 0.15f, VRCTweenEase.OutQuad);
}

void OnDestroy()
{
// Clean up tweens when object is destroyed.
button.KillAllTweens();
}
}

Best Practices​

Store Tween Handles​

Save tween handles if you need to control them later:

private VRCTweenHandle myTween;

void Start()
{
myTween = gameObject.TweenPosition(Vector3.up, 2f, VRCTweenEase.OutQuad);
}

public void StopTween()
{
myTween.Kill();
}

Clean Up Tweens​

Tweens are automatically cleaned up when they complete. However, tweens that are still running (e.g., infinite loops or long-duration tweens) should be killed when their object is destroyed:

void OnDestroy()
{
gameObject.KillAllTweens();
}

Chain Tweens with Callbacks​

Create sequences by using callbacks and custom event names:

void Start()
{
cube.TweenPosition(Vector3.up * 5, 1f, VRCTweenEase.OutQuad)
.OnComplete(this, nameof(OnFirstTweenComplete));
}

public void OnFirstTweenComplete()
{
cube.TweenRotation(new Vector3(0, 180, 0), 1f, VRCTweenEase.InOutQuad)
.OnComplete(this, nameof(OnSecondTweenComplete));
}

public void OnSecondTweenComplete()
{
Debug.Log("Sequence complete!");
}

Use DelayedCall for Cancelable Timers​

VRCTween.DelayedCall is a cancelable alternative to SendCustomEventDelayedSeconds:

private VRCTweenHandle timerHandle;

void Start()
{
// Schedule a delayed callback.
timerHandle = VRCTween.DelayedCall(this, nameof(OnTimerFinished), 3.0f);
}

public void CancelTimer()
{
// Cancel the timer at any time.
timerHandle.Kill();
}

public void OnTimerFinished()
{
Debug.Log("3 seconds have passed!");
}

Udon Graph​

VRCTween works in Udon Graph. Look for the VRCTween and VRCTweenHandle nodes in the node search:

  1. Search for "VRCTween TweenPosition" (or any creation method) to create a tween.
  2. Connect your GameObject and parameters.
  3. The node outputs a VRCTweenHandle.
  4. Use VRCTweenHandle nodes (Kill, Pause, SetDelay, OnComplete, etc.) to control and configure it.

For callbacks, use the VRCTweenHandle OnComplete node:

  • Connect the tween handle and your UdonBehaviour (usually "this").
  • Provide a custom event name string.
  • Implement the corresponding custom event in your graph.

Limitations​

  • Tweens are local to each player and don't sync across the network automatically.
  • For networked animations, consider using Udon's networking features alongside tweens.
  • Very short tween durations (less than 0.01 seconds) may not animate smoothly.
  • Tween handles are unique per scene instance.

Input Validation​

VRCTween rejects values that would corrupt DOTween state or Unity transforms. Rejections do not raise exceptions to avoid crashing the containing UdonBehavior, so a tween that "did nothing" usually means one of the inputs below was invalid.

Creation methods return an invalid handle (and skip creating the tween) when:

  • target is null.
  • duration is negative, NaN, or Infinity. Zero is allowed for creating reusable tweens.
  • A position, scale, or path waypoint contains NaN, Infinity, or a component whose absolute value exceeds roughly 520,000 units. Rotation tweens only require finite values; large euler angles are allowed.
  • A path's resolution is silently clamped to the range 1–50; values outside that range still create the tween, just with a clamped resolution.

Config and control methods silently no-op when given invalid arguments:

  • SetDuration ignores negative, NaN, or Infinity values. Zero is allowed.
  • SetDelay ignores negative, NaN, or Infinity values.
  • Goto ignores NaN or Infinity, and otherwise clamps to the tween's duration.
  • ChangeEndValue ignores floats/vectors that fail the same finite/magnitude checks used at creation.

Check handle.IsValid after creation if you need to branch on whether a tween was actually started.

Performance Tips​

General Tips​

  • Use KillAllTweens() to clean up all tweens on an object at once.
  • Avoid creating hundreds of simultaneous tweens. Stagger them across frames to spread the load (see example below).
  • For most worlds (5 to 50 tweens triggered by interactions), creating fresh tweens is perfectly fine. DOTween pools tween objects internally, so the overhead is minimal.

Staggering Many Tweens​

If you need to animate a large number of objects like a grid of tiles, avoid creating all tweens in the same frame. Instead, spread them over time with a small delay between each:

[SerializeField] private GameObject[] tiles;
[SerializeField] private float delayBetween = 0.05f;

private int _nextIndex;

public void AnimateAll()
{
_nextIndex = 0;
_AnimateNext();
}

public void _AnimateNext()
{
if (_nextIndex >= tiles.Length) return;

tiles[_nextIndex].TweenScale(Vector3.one, 0.3f, VRCTweenEase.OutBack);
_nextIndex++;

SendCustomEventDelayedSeconds(nameof(_AnimateNext), delayBetween);
}

This creates one tween per call, spreading the work across many frames. The visual result is a staggered cascade rather than everything changing at once, which could be a more interesting look anyway!

Reuse Tweens in Hot Paths​

If you have tweens that redirect frequently, such as UI elements following a moving target, or objects responding to player input every frame, reusing handles with ChangeEndValue, SetDuration, and SetEase avoids per-frame allocations. In internal benchmarks with 500 tweens updating over 300 frames, the reuse pattern allocated 46× less memory and ran 10× faster than kill-and-recreate.

This optimization matters when tweens are created and destroyed at high frequency. For one-shot animations (doors, buttons, scale pops), simple kill-and-create code is easier to read and works great.

VRCTweenHandle _moveHandle;

void Start()
{
// Create once with infinite loops so it stays alive after completing.
_moveHandle = gameObject.TweenPosition(Vector3.zero, 1f, VRCTweenEase.OutQuad)
.SetLoops(-1, VRCTweenLoopType.Restart)
.Pause();
}

public void MoveTo(Vector3 target, float duration)
{
// Reconfigure and restart without allocating.
_moveHandle.ChangeEndValue(target, true)
.SetDuration(duration)
.SetEase(VRCTweenEase.OutCubic);
_moveHandle.Restart();
}

See Tween reuse in Settings and Control for more details.

Resources​