---
title: UI animation/tweens
description: Explains how to animate GuiObjects using the process of tweening.
---
In animation, **tweening** is the process of generating intermediate frames between two key points in a sequence. When designing a user interface, you can use tweening to transition a `Class.GuiObject` smoothly from one state to another, such as:
- Smoothly increasing the size of a button when a user selects it.
- Sliding UI menus in and out from the screen edges.
- Gradually animating a health bar between two widths when a user receives a health boost.
## Single-property tweens
### Position
To tween the **position** of a `Class.GuiObject`:
1. Set the `Class.GuiObject.AnchorPoint|AnchorPoint` for the object.
1. Determine `Datatype.UDim2` coordinates for the object's target position, using the **scale** parameters of `Datatype.UDim2` instead of exact pixel values so that the object tweens to the exact center of the screen.
1. Pass a `Datatype.TweenInfo` and the target position to `Class.TweenService:Create()`.
1. Play the tween with `Class.Tween:Play()`.
The following code snippet moves an `Class.ImageLabel` within a `Class.ScreenGui` to the exact center of the screen:
```lua title="UI Tween - Position" highlight="8,10,12,13,15"
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local object = ScreenGui:WaitForChild("ImageLabel")
object.AnchorPoint = Vector2.new(0.5, 0.5)
local targetPosition = UDim2.new(0.5, 0, 0.5, 0)
local tweenInfo = TweenInfo.new(2)
local tween = TweenService:Create(object, tweenInfo, {Position = targetPosition})
tween:Play()
```
### Size
To tween the **size** of a `Class.GuiObject`:
1. Determine `Datatype.UDim2` coordinates for the object's target size, using the **scale** parameters of `Datatype.UDim2` instead of exact pixel values so that the object tweens to a relative percentage of the screen size.
1. Attach a `Class.UIAspectRatioConstraint` to the object to maintain its designed aspect ratio when tweening.
1. Pass a `Datatype.TweenInfo` and the target size to `Class.TweenService:Create()`.
1. Play the tween with `Class.Tween:Play()`.
The following code snippet scales an `Class.ImageLabel` within a `Class.ScreenGui` to 40% of the screen width or height (whichever is smaller) from the object's center anchor point:
```lua title="UI Tween - Size" highlight="8,10,11,13,16,18"
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local object = ScreenGui:WaitForChild("ImageLabel")
object.AnchorPoint = Vector2.new(0.5, 0.5)
local aspectRatioConstraint = Instance.new("UIAspectRatioConstraint")
aspectRatioConstraint.Parent = object
local targetSize = UDim2.new(0.4, 0, 0.4, 0)
local tweenInfo = TweenInfo.new(2)
local tween = TweenService:Create(object, tweenInfo, {Size = targetSize})
tween:Play()
```
### Rotation
To tween the **rotation** of a `Class.GuiObject`:
1. Set the `Class.GuiObject.AnchorPoint|AnchorPoint` for the object to rotate around.
1. Determine the target `Class.GuiObject.Rotation|Rotation` for the object.
1. Pass a `Datatype.TweenInfo` and the target rotation to `Class.TweenService:Create()`.
1. Play the tween with `Class.Tween:Play()`.
```lua title="UI Tween - Size" highlight="8,10,13,15"
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local object = ScreenGui:WaitForChild("ImageLabel")
object.AnchorPoint = Vector2.new(0.5, 0.5)
local targetRotation = 45
local tweenInfo = TweenInfo.new(2)
local tween = TweenService:Create(object, tweenInfo, {Rotation = targetRotation})
tween:Play()
```
### Transparency
Multiple properties control UI transparency, depending on the object type. You can tween each of these properties individually or combined through a [multi-property tween](#multi-property-tweens). Alternatively, you can tween an object's overall transparency by placing it inside a `Class.CanvasGroup` and tweening the group's `Class.CanvasGroup.GroupTransparency|GroupTransparency`.
| UI Object |
Properties |
| `Class.Frame` |
`Class.GuiObject.BackgroundTransparency|BackgroundTransparency` |
| `Class.TextLabel` |
`Class.GuiObject.BackgroundTransparency|BackgroundTransparency`, `Class.TextLabel.TextTransparency|TextTransparency`, `Class.TextLabel.TextStrokeTransparency|TextStrokeTransparency` |
| `Class.TextButton` |
`Class.GuiObject.BackgroundTransparency|BackgroundTransparency`, `Class.TextButton.TextTransparency|TextTransparency`, `Class.TextButton.TextStrokeTransparency|TextStrokeTransparency` |
| `Class.ImageLabel` |
`Class.GuiObject.BackgroundTransparency|BackgroundTransparency`, `Class.ImageLabel.ImageTransparency|ImageTransparency` |
| `Class.ImageButton` |
`Class.GuiObject.BackgroundTransparency|BackgroundTransparency`, `Class.ImageButton.ImageTransparency|ImageTransparency` |
As a modern alternative to `Class.TextLabel.TextStrokeTransparency` and `Class.TextButton.TextStrokeTransparency`, you can tween stroke transparency on a `Class.UIStroke` object as outlined in [Stroke](#stroke).
```lua title="UI Tween - Image Transparency" highlight="8,11,13"
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local object = ScreenGui:WaitForChild("ImageLabel")
local targetTransparency = 0.8
local tweenInfo = TweenInfo.new(2)
local tween = TweenService:Create(object, tweenInfo, {ImageTransparency = targetTransparency})
tween:Play()
```
```lua title="UI Tween - Canvas Group Transparency" highlight="8,11,13"
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local canvasGroup = ScreenGui:WaitForChild("CanvasGroup")
local targetTransparency = 0.8
local tweenInfo = TweenInfo.new(2)
local tween = TweenService:Create(canvasGroup, tweenInfo, {GroupTransparency = targetTransparency})
tween:Play()
```
### Color
Multiple properties control UI color, depending on the object type. You can tween each of these properties individually or combined through a [multi-property tween](#multi-property-tweens). Alternatively, you can tween an object's overall color by placing it inside a `Class.CanvasGroup` and tweening the group's `Class.CanvasGroup.GroupColor3|GroupColor3`.
| UI Object |
Properties |
| `Class.Frame` |
`Class.GuiObject.BackgroundColor3|BackgroundColor3`, `Class.GuiObject.BorderColor3|BorderColor3` |
| `Class.TextLabel` |
`Class.GuiObject.BackgroundColor3|BackgroundColor3`, `Class.GuiObject.BorderColor3|BorderColor3`, `Class.TextLabel.TextColor3|TextColor3`, `Class.TextLabel.TextStrokeColor3|TextStrokeColor3` |
| `Class.TextButton` |
`Class.GuiObject.BackgroundColor3|BackgroundColor3`, `Class.GuiObject.BorderColor3|BorderColor3`, `Class.TextButton.TextColor3|TextColor3`, `Class.TextButton.TextStrokeColor3|TextStrokeColor3` |
| `Class.ImageLabel` |
`Class.GuiObject.BackgroundColor3|BackgroundColor3`, `Class.GuiObject.BorderColor3|BorderColor3`, `Class.ImageLabel.ImageColor3|ImageColor3` |
| `Class.ImageButton` |
`Class.GuiObject.BackgroundColor3|BackgroundColor3`, `Class.GuiObject.BorderColor3|BorderColor3`, `Class.ImageButton.ImageColor3|ImageColor3` |
As a modern alternative to `Class.GuiObject.BorderColor3`, `Class.TextLabel.TextStrokeColor3`, and `Class.TextButton.TextStrokeColor3`, you can tween stroke color on a `Class.UIStroke` object as outlined in [Stroke](#stroke).
```lua title="UI Tween - Image Color" highlight="8,11,13"
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local object = ScreenGui:WaitForChild("ImageLabel")
local targetColor = Color3.fromRGB(255, 0, 0)
local tweenInfo = TweenInfo.new(2)
local tween = TweenService:Create(object, tweenInfo, {ImageColor3 = targetColor})
tween:Play()
```
```lua title="UI Tween - Canvas Group Color" highlight="8,11,13"
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local canvasGroup = ScreenGui:WaitForChild("CanvasGroup")
local targetColor = Color3.fromRGB(255, 0, 0)
local tweenInfo = TweenInfo.new(2)
local tween = TweenService:Create(canvasGroup, tweenInfo, {GroupColor3 = targetColor})
tween:Play()
```
### Stroke
Multiple properties control UI borders, depending on the object type.
| UI object |
Properties |
| `Class.Frame` |
`Class.GuiObject.BorderSizePixel|BorderSizePixel`, `Class.GuiObject.BorderColor3|BorderColor3` |
| `Class.TextLabel` |
`Class.GuiObject.BorderSizePixel|BorderSizePixel`, `Class.GuiObject.BorderColor3|BorderColor3`, `Class.TextLabel.TextStrokeColor3|TextStrokeColor3`, `Class.TextLabel.TextStrokeTransparency|TextStrokeTransparency` |
| `Class.TextButton` |
`Class.GuiObject.BorderSizePixel|BorderSizePixel`, `Class.GuiObject.BorderColor3|BorderColor3`, `Class.TextButton.TextStrokeColor3|TextStrokeColor3`, `Class.TextButton.TextStrokeTransparency|TextStrokeTransparency` |
| `Class.ImageLabel` |
`Class.GuiObject.BorderSizePixel|BorderSizePixel`, `Class.GuiObject.BorderColor3|BorderColor3` |
| `Class.ImageButton` |
`Class.GuiObject.BorderSizePixel|BorderSizePixel`, `Class.GuiObject.BorderColor3|BorderColor3` |
Alternatively, you can apply a `Class.UIStroke` child and tween its thickness, color, and/or transparency.
| UI Object |
Properties |
| `Class.UIStroke` |
`Class.UIStroke.Color|Color`, `Class.UIStroke.Thickness|Thickness`, `Class.UIStroke.Transparency|Transparency` |
```lua title="UI Tween - UIStroke Color & Thickness" highlight="8-11,13,14,17,19"
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local object = ScreenGui:WaitForChild("TextLabel")
local stroke = Instance.new("UIStroke")
stroke.Color = Color3.fromRGB(255, 255, 255)
stroke.Thickness = 5
stroke.Parent = object
local targetColor = Color3.fromRGB(255, 0, 0)
local targetThickness = 10
local tweenInfo = TweenInfo.new(2)
local tween = TweenService:Create(stroke, tweenInfo, {Color = targetColor, Thickness = targetThickness})
tween:Play()
```
## Multi-property tweens
You can combine any of the [single-property tweens](#single-property-tweens) into more complex tweens by passing multiple target properties to `Class.TweenService:Create()`, for example **position + rotation** or **size + transparency**.
```lua title="UI Tween - Position & Rotation" highlight="8,10,11,14,16"
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local object = ScreenGui:WaitForChild("ImageLabel")
object.AnchorPoint = Vector2.new(0.5, 0.5)
local targetPosition = UDim2.new(0.5, 0, 0.5, 0)
local targetRotation = 45
local tweenInfo = TweenInfo.new(2)
local tween = TweenService:Create(object, tweenInfo, {Position = targetPosition, Rotation = targetRotation})
tween:Play()
```
```lua title="UI Tween - Size & Transparency" highlight="13,14,17,19"
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local object = ScreenGui:WaitForChild("ImageLabel")
object.AnchorPoint = Vector2.new(0.5, 0.5)
local aspectRatioConstraint = Instance.new("UIAspectRatioConstraint")
aspectRatioConstraint.Parent = object
local targetSize = UDim2.new(0.4, 0, 0.4, 0)
local targetTransparency = 0.8
local tweenInfo = TweenInfo.new(2)
local tween = TweenService:Create(object, tweenInfo, {Size = targetSize, ImageTransparency = targetTransparency})
tween:Play()
```
## Tween sequences
You can chain UI animations to occur one after another by playing subsequent tweens upon the previous tween's `Class.TweenBase.Completed|Completed` event. For example, the following script moves an object to the center of the screen, then rotates it by 45°.
```lua title="UI Tween Sequence" highlight="10,11,14,15,18,21-23"
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local ScreenGui = PlayerGui:WaitForChild("ScreenGui")
local object = ScreenGui:WaitForChild("ImageLabel")
object.AnchorPoint = Vector2.new(0.5, 0.5)
local targetPosition = UDim2.new(0.5, 0, 0.5, 0)
local targetRotation = 45
local tweenInfo = TweenInfo.new(2)
local positionTween = TweenService:Create(object, tweenInfo, {Position = targetPosition})
local rotationTween = TweenService:Create(object, tweenInfo, {Rotation = targetRotation})
-- Initially play position tween
positionTween:Play()
-- Play rotation tween upon completion of position tween
positionTween.Completed:Connect(function()
rotationTween:Play()
end)
```
## Easing options
Using the easing options of `Datatype.TweenInfo`, you can control the easing **style** and **direction** of UI animations.
### Style
`Enum.EasingStyle` sets the rate of interpolation from start to end. By default, easing style is set to `Enum.EasingStyle.Quad`.
| Style |
Description |
| **Linear** |
Moves at a constant speed. |
| **Sine** |
Speed is determined by a sine wave for a gentle easing motion. |
| **Quad** |
Similar to **Sine** but with a slightly sharper curve based on quadratic interpolation. |
| **Cubic** |
Similar to **Quad** but with a slightly sharper curve based on cubic interpolation. |
| **Quart** |
Similar to **Cubic** but with an even sharper curve based on quartic interpolation. |
| **Quint** |
Similar to **Quart** but with an even sharper curve based on quintic interpolation. |
| **Exponential** |
The sharpest curve based on exponential interpolation. |
| **Circular** |
Follows a circular arc, such that acceleration is more sudden and deceleration more gradual versus **Quint** or **Exponential**. |
| **Back** |
Slightly overshoots the target, then backs into place. |
| **Bounce** |
Bounces backwards multiple times after reaching the target, before eventually settling. |
| **Elastic** |
Moves as if attached to a rubber band, overshooting the target several times. |
```lua title="Easing Style - Cubic" highlight="1"
local tweenInfo = TweenInfo.new(2, Enum.EasingStyle.Cubic)
local tween = TweenService:Create(object, tweenInfo, {Rotation = 45})
```
### Direction
`Enum.EasingDirection` defines how the [easing style](#style) interpolation applies to an object, with a default of **Out**. Note that a tween with **Linear** easing style is not affected, as linear interpolation is constant from start to end.
| Direction |
Description |
| **In** |
The easing style applies in a forward direction. |
| **Out** |
The easing style applies in a reverse direction. |
| **InOut** |
The easing style applies forward for the first half and in reverse for the second half. |
```lua title="Easing direction - InOut" highlight="1"
local tweenInfo = TweenInfo.new(2, Enum.EasingStyle.Cubic, Enum.EasingDirection.InOut)
local tween = TweenService:Create(object, tweenInfo, {Rotation = 45})
```
## Animate text
You can easily enhance text-based UI, such as cutscene banners, player instructions, and prompts, with animated effects.
### Typewriter effect
The "typewriter" effect is ideal for `Class.TextLabel|TextLabels` that tell a story, output NPC conversations, etc.
1. Create a new `Class.ModuleScript` within **ReplicatedStorage**.
1. Rename the new script **AnimateUI**.
1. Paste the following code into the script:
```lua title="ModuleScript - AnimateUI"
local LocalizationService = game:GetService("LocalizationService")
local Players = game:GetService("Players")
local SOURCE_LOCALE = "en"
local translator = nil
local AnimateUI = {}
function AnimateUI.loadTranslator()
pcall(function()
translator = LocalizationService:GetTranslatorForPlayerAsync(Players.LocalPlayer)
end)
if not translator then
pcall(function()
translator = LocalizationService:GetTranslatorForLocaleAsync(SOURCE_LOCALE)
end)
end
end
function AnimateUI.typeWrite(guiObject, text, delayBetweenChars)
guiObject.Visible = true
guiObject.AutoLocalize = false
local displayText = text
-- Translate text if possible
if translator then
displayText = translator:Translate(guiObject, text)
end
-- Replace line break tags so grapheme loop will not miss those characters
displayText = displayText:gsub("
", "\n")
-- Remove RichText tags since char-by-char animation will break the tags
displayText = displayText:gsub("<[^<>]->", "")
-- Set translated/modified text on parent
guiObject.Text = displayText
local index = 0
for first, last in utf8.graphemes(displayText) do
index += 1
guiObject.MaxVisibleGraphemes = index
task.wait(delayBetweenChars)
end
end
return AnimateUI
```
If your experience's **source language** is not English (`"en"`), change the locale code for `SOURCE_LOCALE` to match the source language in [localization settings](../production/localization/index.md#localization-settings).
1. Create a `Class.TextLabel` in a suitable location, such as within a `Class.ScreenGui` parented to `Class.StarterGui`.
1. Insert a new `Class.LocalScript` as a direct child of the label and paste in the following code. Note that each message is output by calling `AnimateUI.typeWrite()` with parameters for the parent object, the string to output, and the delay between characters.
```lua title="LocalScript" highlight="3,5,10,11,15,16"
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local AnimateUI = require(ReplicatedStorage:WaitForChild("AnimateUI"))
local label = script.Parent
-- Load translator if experience is localized
--AnimateUI.loadTranslator()
local message1 = [[Beyond this door is the
Great Zorgoth... 🗡]]
AnimateUI.typeWrite(label, message1, 0.05)
task.wait(1)
local message2 = [[...who rules this dungeon unchallenged! 😈]]
AnimateUI.typeWrite(label, message2, 0.05)
```