How to Tween Parts in Roblox (TweenService Guide)
Quick answer: Use TweenService:Create(part, TweenInfo.new(...), { Position = target }) and :Play() — the engine interpolates the chosen properties smoothly every frame, which is cheaper and smoother than moving parts in a loop.
To move a part smoothly in Roblox, create a tween with TweenService:Create(), pass a TweenInfo describing the motion, and call :Play(). The engine interpolates the properties you choose (position, size, color, transparency) frame by frame, which is far smoother and cheaper than updating the part yourself every frame.
Why not just change CFrame in a loop?
A common beginner pattern is a while loop that nudges a part's CFrame a tiny bit each iteration. It works, but it fights the engine:
- You must manage your own timing with task.wait() vs wait(), and any drift or hiccup in the loop shows up as stutter.
- Interpolation quality depends on your math; easing curves like ease-in-out have to be hand-rolled.
- Server-side loops burn bandwidth because every CFrame update replicates.
TweenService solves all three: it interpolates on the render step with proper delta time and lets you pick professional easing styles for free.
Creating a tween with TweenService:Create
The API has one main constructor:
local TweenService = game:GetService("TweenService")
local part = workspace.MovingPlatform
local tweenInfo = TweenInfo.new(
2, -- duration in seconds
Enum.EasingStyle.Quad, -- easing style
Enum.EasingDirection.Out, -- easing direction
0, -- repeat count (-1 = forever)
false, -- reverses (plays backward on repeat)
0 -- delay before each repetition
)
local goal = { Position = Vector3.new(20, 5, 0) }
local tween = TweenService:Create(part, tweenInfo, goal)
tween:Play()
TweenService:Create(instance, tweenInfo, goalProperties) takes the instance to animate, the timing profile, and a table of property/value pairs to end at. The start values are captured when you call Play().
Understanding TweenInfo parameters
Every tween's feel comes from its TweenInfo:
| Parameter | Meaning |
|---|---|
| Time | Duration of one playthrough, in seconds |
| EasingStyle | The curve: Linear, Sine, Quad, Cubic, Back, Bounce, Elastic |
| EasingDirection | Which way along the curve: In, Out, InOut |
| RepeatCount | Extra repetitions after the first play (-1 loops forever) |
| Reverses | If true, each repeat plays in reverse (ping-pong) |
| DelayTime | Seconds to wait before starting, and between repeats |
Enum.EasingStyle.Linear is right for constant-speed conveyor-like movement; Sine or Quad with Out feels natural for UI and doors; Back and Elastic add playful overshoot.
Complete example: moving a part and changing its color
Tweens interpolate multiple properties at once — just list them in the goal table:
-- LocalScript or Script inside a Part named "Treasure"
local TweenService = game:GetService("TweenService")
local part = script.Parent
part.Anchored = true -- required for reliable CFrame tweens
local info = TweenInfo.new(1.5, Enum.EasingStyle.Quart, Enum.EasingDirection.InOut)
local tween = TweenService:Create(part, info, {
CFrame = CFrame.new(Vector3.new(0, 10, -30)) * CFrame.Angles(0, math.rad(90), 0),
Color = Color3.fromRGB(255, 200, 50),
})
tween:Play()
tween.Completed:Connect(function(state)
if state == Enum.PlaybackState.Completed then
print("Arrived at destination")
end
end)
Prefer tweening CFrame over Position when possible: it moves and rotates together in one property and avoids gimbal-style surprises.
Playing, pausing, and cancelling
A tween object gives you playback control:
tween:Play() -- starts or resumes
tween:Pause() -- freezes at the current value
tween:Cancel() -- stops and resets progress to zero
You can also read tween.PlaybackState, which is one of Delayed, Playing, Paused, Cancelled, or Completed. Calling Play() on an already-playing tween restarts it from the current values.
Note: a tween only animates while its instance exists and the script's context allows it. Destroying the part cancels the tween automatically.
Chaining with the Completed event
For sequences — a platform that travels out, waits, comes back — chain tweens through the Completed event instead of nesting waits:
local TweenService = game:GetService("TweenService")
local platform = workspace.Platform
platform.Anchored = true
local info = TweenInfo.new(3, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut)
local goTween = TweenService:Create(platform, info, {
Position = Vector3.new(40, 4, 0),
})
local backTween = TweenService:Create(platform, info, {
Position = Vector3.new(0, 4, 0),
})
goTween.Completed:Connect(function(state)
if state == Enum.PlaybackState.Completed then
backTween:Play()
end
end)
goTween:Play()
Always check the final state in the handler: Cancel() also fires Completed, and you usually do not want a cancelled tween to trigger the next leg. For more complex sequencing, combine this with task.spawn or coroutines.
Common mistakes to avoid
Tweening CFrame on an unanchored part. If a part is free to fall or be pushed by physics, the physics solver and the tween both fight over its position and the result jitters. Anchor any part whose position you tween, or tween an offset inside a welded assembly rather than the root.
Tweening physics-driven characters. Player characters and NPCs driven by Humanoid should not have their root CFrame tweened directly — it breaks collision and replication. Move characters with pathfinding or velocity-based approaches instead.
Creating a new tween every frame. Build the tween once and reuse it with Play(); constructing hundreds per second leaks instances and stutters.
Expecting instant results after Cancel. Cancel() resets progress, but the part keeps whatever value it had when cancelled — reset it yourself if needed.
Firing server changes without remotes. Tweens run locally on whichever machine plays them. To make a client-side tween visible to everyone, either run it on the server or sync gameplay state through RemoteEvents.
Learn Luau by doing
Reading about TweenService only gets you so far — the fastest way to internalize easing styles and chaining is to experiment. Work through the theory at /theory, practice with guided challenges at /problems, and follow structured Luablox lessons to build these habits early.
Frequently asked questions
Do parts need to be Anchored to tween them in Roblox?
Yes, for CFrame or Position tweens the part should be Anchored. Unanchored parts are controlled by the physics simulation, so the tween and the physics solver fight over position, causing jitter. Anchored parts are moved purely by the tween and stay perfectly smooth.
What is the difference between TweenInfo time and delayTime?
Time is how long one playthrough of the tween lasts in seconds. DelayTime is a pause inserted before the tween starts and again before each repeat. A tween with time 2, repeatCount 2, and delayTime 1 runs twice with a 1-second pause between them.
Can I tween multiple properties at once?
Yes. Put every target property in the goal table passed to TweenService:Create, such as { CFrame = ..., Color = ..., Transparency = 0.5 }. All listed properties interpolate simultaneously over the same TweenInfo duration.
How do I run code after a tween finishes?
Connect to the tween.Completed event. The callback receives the final PlaybackState, so check that it equals Enum.PlaybackState.Completed before triggering follow-up logic — Cancel also fires Completed.
Why does my tweened part look different on other players screens?
A tween only animates on the machine where :Play() was called. If a client plays a tween, other players never see it. Play the tween on the server, or keep it cosmetic on the client and replicate gameplay state with RemoteEvents.
Master 3D spatial math in Roblox, including CFrames, Vectors, and coordinate system manipulation.