Getting Things to Move Without Writing a Loop
Most people start animation in Roblox by writing their own frame-by-frame loops or stacking coroutines and hoping they line up. I did that for about six months before I actually read the docs on what was already built in. TweenService is the standard way to handle transitions between two states over time. It handles the easing, the duration, and the interpolation so you don't have to. The basic call looks like this. You get the service, define a target property and value, set a duration, and it does the math each frame. Here is what that actually looks like in code:
local TweenService = game:GetService("TweenService")
local info = TweenInfo.new(
1, -- seconds
Enum.EasingStyle.Quad,
Enum.EasingDirection.InOut,
0, -- repeats
false, -- reverses
0 -- delay
)
local goal = {Position = Vector3.new(10, 5, 10)}
local tween = TweenService:Create(part, info, goal)
tween:Play()
That part moves from wherever it is to position 10, 5, 10 over one second using a quadratic ease in and out. Simple enough. The thing people miss is that goal is just a dictionary of properties. You can tween any numeric or vector property that the instance supports. That includes Position, Rotation, Transparency, Size, BrickColor properties, and UDim values on GUI objects. You do not need to tween everything at once though. Pass only what you care about and the rest stays untouched. There is a specific edge case that cost me about three hours of debugging once. I was tweening a BillboardGui's ScaleSize property to make it grow when a player got close. The tween played perfectly on the client, looked great, and then the moment I moved the logic server-side, the billboard would snap back to its original size every single frame. The issue was that the server was not replicating the visual property changes the same way the client did. Billboards and their scale are client-rendered in many contexts, so a server-only tween either gets overridden or behaves inconsistently across connections. I moved the tween creation to a LocalScript, passed the player reference instead of the object itself, and the snapping stopped. TweenService works fine server-side for regular parts and models. It is just the GUI and billboard cases where you need to be careful about which side runs the tween. Another thing nobody talks about much. TweenService does not support tweening non-numeric or non-continuous properties. You cannot tween Color3 by name, you cannot tween Enums, and you cannot tween something like Material directly through a goal table. If you want to change a color, you tween the R, G, B values separately or use a different approach entirely. People try to tween CFrame sometimes and it works, but it is often better to tween Position instead. CFrame tweens can introduce unexpected rotation behavior if the part has any child welds or constraints attached, and debugging that is annoying.
The TweenInfo constructor takes more than just duration. The repeat count is one of the most useful parameters. Set it to -1 and the tween loops forever. Set it to 2 and it plays three times total. The reverse flag is also worth knowing. Set it to true and the tween plays forward then immediately backward, which saves you from creating a second tween object just to return something to its starting state. Here is a practical example of using repeats and reverse for a door that opens and closes on a timer:
Get the Full Details

local info = TweenInfo.new(
0.8,
Enum.EasingStyle.Linear,
Enum.EasingDirection.Out,
-1,
true
)
local goal = {CFrame = part.CFrame * CFrame.Angles(0, math.rad(90), 0)}
local tween = TweenService:Create(part, info, goal)
tween:Play()
This opens the door 90 degrees, waits 0.8 seconds, closes it back, waits again, and repeats forever. No while loop. No connection cleanup needed unless you want to stop it for some reason. If you do need to stop it mid-sequence, call tween:Cancel(). That stops the current play and resets the property to its original value before the tween started. If you want it to stay where it is when you cancel, store the original state yourself or use Completed events to intercept the end of a cycle. Event handling on tweens is straightforward but easy to overlook. Every tween fires three events. Started fires when the tween begins. Completed fires when it finishes normally. PropertyChanged fires on the tween object itself whenever internal properties change. Listening to Completed is how you chain tweens without nesting callbacks or using coroutines:
tween.Completed:Connect(function()
-- chain to next tween here
end)
Performance is not a real concern for TweenService unless you are creating hundreds of concurrent tweens on the same object. The engine handles interpolation on the client render thread for GUI objects and on the main thread for workspace parts. Typical usage, like menu animations, health bars, UI transitions, and simple part movement, uses negligible resources. The bottleneck is usually not TweenService itself but rather how many tweens you fire at once during a single frame. If you are building a complex UI with dozens of animated elements, space them out using staggered delays rather than firing them all on the same frame. A delay of even 0.05 seconds between each tween makes a noticeable difference in frame consistency on lower-end devices. The one scenario where TweenService genuinely fails is when you need frame-dependent logic during the animation. Say you want an object to change color halfway through its move, or trigger an event at a specific point in the tween. TweenService has no built-in keyframe event system like some animation engines do. Your workaround is to hook into RunService.RenderStepped and calculate the progress manually using the tween's ElapsedTime divided by the total Duration. It is clunky but it works. Most people just don't realize they need this until they are already mid-project. For those cases I usually write a small utility function that accepts a tween and a callback, then uses RenderStepped to fire the callback when the elapsed time passes a threshold. It takes about twenty lines and saves you from refactoring your whole animation system later.
Bottom line: TweenService covers the vast majority of animation needs in Roblox without requiring custom loops or physics tricks. Learn the TweenInfo parameters properly. Watch out for client-server mismatches on GUI objects. And don't try to force it to handle things it was never designed for.
