A lightweight, tween utility for Roblox games.
No boilerplate. No headaches. Just smooth animations.
- Parent the modules from /src inside of Init in ReplicatedStorage.
- Require it wherever you need it:
local Smoothly = require(game.ReplicatedStorage.Init)You can rename Init to Smoothly for better finding.
-- Move a part up over 2 seconds with a bounce
Smoothly.to(part, 2, { Position = Vector3.new(0, 50, 0) }, "easeOutBounce")
-- Fade in from invisible
Smoothly.from(part, 1, { Transparency = 1 }, "easeOutQuad")
-- Delay something by half a second
Smoothly.delay(0.5, function()
print("half a second later!")
end)Tweens an object from its current values to the given properties.
Smoothly.to(part, 1.5, {
Position = Vector3.new(0, 20, 0),
Transparency = 0.5,
}, "easeInOutQuad")Returns a Tween object.
Tweens an object from the given values to its current state.
Great for intro animations, set where things start, not where they end.
Smoothly.from(frame, 0.3, { Size = UDim2.new(0, 0, 0, 0) }, "easeOutBack")Returns a Chain builder for sequencing tweens one after another.
Smoothly.chain(part)
:to(1, { Position = Vector3.new(0, 10, 0) }, "linear")
:wait(0.5)
:to(1, { Position = Vector3.new(0, 50, 0) }, "easeInOutQuad")
:call(function() print("midpoint!") end)
:to(0.5, { Transparency = 1 }, "easeOutSine")
:onComplete(function() print("chain done!") end)
:play()Chain methods:
| Method | Description |
|---|---|
:to(duration, props, easing) |
Add a forward tween step |
:from(duration, props, easing) |
Add a tween-from step |
:wait(duration) |
Pause between steps |
:call(fn) |
Run a function between steps |
:onStart(fn) |
Called when the chain begins |
:onComplete(fn) |
Called when all steps finish |
:play() |
Start the chain |
Runs multiple tweens at the same time. Fires onComplete when all finish.
Smoothly.parallel({
{ part1, 1, { Size = Vector3.new(5, 5, 5) }, "easeOutQuad" },
{ part2, 1, { Transparency = 0 }, "linear" },
{ part3, 2, { Position = Vector3.new(0, 30, 0) }, "easeInOutCubic" },
}, function()
print("all done!")
end)Each entry can also be a named table:
{ obj = part, duration = 1, props = { Transparency = 0 }, easing = "linear" }Shorthand for defining a chain of tweens on one object in a single table.
Smoothly.sequence(part, {
{ duration = 0.5, props = { Position = Vector3.new(0, 10, 0) }, easing = "linear" },
{ duration = 1, props = { Position = Vector3.new(0, 50, 0) }, easing = "easeInOutQuad" },
{ duration = 0.5, props = { Transparency = 1 }, easing = "easeOutSine", wait = 0.2 },
}, {
onComplete = function() print("sequence finished") end,
})Each step supports:
durationhow long the tween lastsprops/propertiestarget property valueseasingeasing name or functionwaitoptional pause after this step (seconds)afteroptional callback to run after this step
Fires a callback after a set time. Lightweight animation scheduler.
Smoothly.delay(2, function()
part.BrickColor = BrickColor.new("Bright red")
end)Register a custom easing function globally so you can reference it by name anywhere.
Smoothly.ease("myBounce", function(t)
return math.sin(t * math.pi)
end)
Smoothly.to(part, 1, { Position = Vector3.new(0, 20, 0) }, "myBounce")Custom easing functions receive t (0–1) and must return a number (usually 0–1, but overshooting is fine).
Reverses the direction of a playing tween.
local t = Smoothly.to(part, 3, { Position = Vector3.new(0, 50, 0) }, "linear")
task.wait(1)
Smoothly.reverse(t.id)Smoothly.pause(tweenId) -- pause a specific tween
Smoothly.resume(tweenId) -- resume a specific tween
Smoothly.cancel(tweenId) -- cancel a specific tween
Smoothly.pauseAll() -- pause everything
Smoothly.pauseAll("ui") -- pause only tweens tagged "ui"
Smoothly.resumeAll() -- resume everything
Smoothly.resumeAll("ui") -- resume only tagged tweens
Smoothly.cancelAll() -- cancel everything
Smoothly.cancelAll("effects") -- cancel only tagged tweens
Smoothly.count() -- number of active tweens
Smoothly.count("ui") -- active tweens with a given tagTags are set through the options table:
Smoothly.to(frame, 0.3, { Transparency = 0 }, "easeOutQuad", { tag = "ui" })Every Smoothly.to and Smoothly.from call returns a Tween object you can interact with directly.
local t = Smoothly.to(part, 2, { Transparency = 1 }, "linear")
t:pause()
t:resume()
t:cancel()
t:reverse()
t:reset()
t:onStart(function() print("started") end)
t:onUpdate(function(progress) print(progress) end)
t:onComplete(function() print("done") end)
print(t:getState()) -- "playing", "paused", "completed", "cancelled"
print(t:getProgress()) -- 0 to 1
print(t.id) -- unique id stringAll tween functions accept an optional options table as the last argument:
| Key | Type | Description |
|---|---|---|
tag |
string | Label this tween for group control |
loop |
boolean | Loop forever |
pingPong |
boolean | Reverse back and forth |
repeatCount |
number | Repeat N times (0 = no repeats) |
Smoothly.to(part, 1, { Transparency = 0.5 }, "easeInOutSine", {
loop = false,
pingPong = true,
repeatCount = 3,
tag = "ambient",
})| Name | Curve |
|---|---|
linear |
Constant speed |
easeInQuad / easeOutQuad / easeInOutQuad |
Smooth quadratic |
easeInCubic / easeOutCubic / easeInOutCubic |
Stronger curve |
easeInQuart / easeOutQuart / easeInOutQuart |
Even stronger |
easeInSine / easeOutSine / easeInOutSine |
Gentle, sine-based |
easeInExpo / easeOutExpo / easeInOutExpo |
Dramatic acceleration |
easeInCirc / easeOutCirc / easeInOutCirc |
Circular arc |
easeInBack / easeOutBack / easeInOutBack |
Slight overshoot |
easeInBounce / easeOutBounce / easeInOutBounce |
Bouncy landing |
easeInElastic / easeOutElastic / easeInOutElastic |
Spring-like snap |
You can also pass a function directly instead of a name string:
Smoothly.to(part, 1, { Transparency = 0 }, function(t)
return t * t * t
end)Smoothly can animate any property of these types:
numberVector3,Vector2CFrameColor3UDim2,UDimNumberRange
src/
├── init.luau main API entry point
├── tween.luau core tween object
├── chain.luau sequential chaining
├── parallel.luau simultaneous tweens
├── sequence.luau step-list shorthand
├── easing.luau built-in & custom easings
├── manager.luau tween registry & group control
└── utils.luau lerp, id gen, helpers
Make sure to parent all the modules inside init module for everything to work properly.
This project is licensed under MIT License, check the license file for more.
Happy building! :)