Tween+|Optimized & advanced tweening

Logo

An open-source tweening module for Roblox, featuring advanced
datatypes, accurate color interpolation and more customization.

Module buttonGithub button



:star2: More capabilities!

With all the features of TweenService,
and many new and amazing features:

  • Attributes support.
  • Advanced properties:
    • Pivot.
    • Scale.
    • Sequences.
    • Ranges.
    • Strings.
  • Reset tween method.
  • Framerate limit control.
  • Additional tween signals.
  • Update event customization.
  • Intuitive custom TweenInfo table.
  • Accurate color interpolation (Oklab).

With all easing styles that TweenService provides,
and an additional OutIn easing direction for all styles.

:eyes: Check it out below!

🎬 Showcases

Text tween

A super simple text tween — although not supported at all with TweenService.
Tween+ handles strings cleverly, finding similarities and differences, then interpolating accordingly.

Low FPS

A super simple part movement tween with the FPS set to 10.
The game still runs smoothly (in this case 144 FPS).

Accurate color

A super simple part color tween.
Since Tween+ handles colors using Oklab interpolation, we get a smooth blend unlike any other.

OutIn easing direction

A super simple part movement tween with a rather interesting, exclusive easing direction.

:zap: Efficient, robust, intuitive.

  • Blazingly fast speeds.
  • Clear error feedback.
  • Typing & documentation.

:rocket: Get started!

💡 Tutorial

Getting the module

Let’s first get the module. There are two ways:

  • Get it from the creator store:
    • Click Get at the top of this post.
    • Click Get Model on the store.
    • Open the ToolBox in Roblox Studio.
    • Go to the Inventory tab.
    • Click on Tween+.
  • Get it from GitHub:
    • Click Git at the top of this post.
    • Go to Releases.
    • Download the latest .rbxm file.
    • Find the file in your file explorer.
    • Drag the file into Roblox Studio.

Find a great place for the module in your explorer.

Basics

In any script, simply require the module. It will return a tween creation function. Example:

local tweenPlus = require(script.TweenPlus)

local tween = tweenPlus(
	workspace.Part, -- Instance to tween.
	{} -- Properties to tween.
)

You simply input all the properties you would like to tween.

local tween = tweenPlus(
	workspace.Part,
	{
		Position = Vector3.new(0, 10, 0),
		Color = Color3.fromRGB(255, 255, 0)
		-- And as many more as you'd like.
	}
)

Full list of supported data types:

  • boolean
  • number
  • string
  • Vector2
  • Vector3
  • UDim
  • UDim2
  • Color3
  • CFrame
  • NumberRange
  • NumberSequence
  • ColorSequence

Attributes

Starting a value name off with a @ will let Tween+ know that it’s an attribute.
Other than that they work the same as usual properties.
For example:

local tween = tweenPlus(
	workspace.Part,
	{
		Position = Vector3.new(0, 10, 0),
		Color = Color3.fromRGB(255, 255, 0),
		["@Coolness"] = 10 -- Attribute named 'Coolness'.
	}
)

Customization (TweenInfo)

The customization works with a table, where you can provide any customizations you want.
You don’t have to provide all, or even any, because they all have defaults.

You use it like this:

local tween = tweenPlus(
	workspace.Part,
	{
		Position = Vector3.new(0, 10, 0),
		Color = Color3.fromRGB(255, 255, 0)
	},
	{ -- Customization (optional).
		Time = 10,
		EasingStyle = "Sine",
		EasingDirection = "InOut"
	}
)

Full list of customization options:

  • Time: number

  • EasingStyle: “Linear” | “Quad” | “Cubic” | “Quart” | “Quint” | “Sine” | “Exponential” | “Circular” | “Elastic” | “Back” | “Bounce”
  • EasingDirection: “In” | “Out” | “InOut” | “OutIn”

  • RepeatCount: number
  • Reverses: boolean
  • DelayTime: number

  • FPS: number

  • UpdateEvent: “PostSimulation” | “PreSimulation” | “PreRender”

Playback and control

When creating a new tween, you can then use the following methods:

  • Start
    Starts or resumes playback of the tween.
  • Stop
    Stops playback of the tween.
    Can be resumed with Start().
  • Reset
    Stops playback of the tween, and resets to starting values.
  • Destroy
    Makes the tween unusable, and ensures it’s stopped.

For example:

local tween = tweenPlus(workspace.Part, {Transparency = 1})
tween:Start()

Signals

When creating a new tween, you can listen to the following signals:

  • Updated
    Fires every iteration of a tween playback; every time values are updated for the instance.
    It provides the current alpha (linear) at every update.
    alpha is the progress from a to b — so with reversing and repeats it will go back and forth.
  • Started
    Fires when the tween playback is started or resumed.
  • Stopped
    Fires when the tween playback is stopped.
  • Completed
    Fires when the tween playback finishes.

For example:

local tween = tweenPlus(workspace.Part, {Transparency = 1})
tween.Updated:Connect(function(alpha)
	print("Progress: "..tostring(alpha*100).."%") -- For example "Progress: 50%".
end)
tween:Start()


:bell: Don’t forget to stay up-to-date!

It’s highly recommended to have the latest version at all times. This ensures:

  • Newest features.
  • Best performance.
  • Little to no bugs.

You can view all releases (versions) and what they contain at the GitHub repository.
Major updates will be posted here, in the replies section, too.

Use this post as documentation for the module.
All documentation will stay up-to-date in this post.

:loudspeaker: Share your thoughts and creations!

I’d love to see what you guys are able to do with this, so consider sharing your works!

But most importantly:

  • Report any bugs or mistakes you find for this asset and post!
  • Consider providing feedback to help me and the asset improve!



Like what you're seeing?
Check out more from me!

∙ ​ Text+|Custom fonts & advanced control
∙ ​ Signal+|Insanely optimized script signal

Tags

module animation tween tweenservice attributes support advanced properties fps framerate limit control accurate color interpolation oklab open source quality performance optimization

8 Likes

Is there supposed to be example attachments here? They aren’t showing up for me.


Cool module!

Yes, there are supposed to be videos there.
They work perfectly fine for me, so that’s weird.
Which browser are you using?

I’m sorry but… why?

Tween+

local tweenPlus = require(tweenPlusPath)

local tween = tweenPlus(
    workspace.Part
    {
        Position = Vector3.new(0, 10, 0),
        Color = Color3.fromRGB(255, 255, 0)
    },
    { -- Customization (optional).
        Time = 10,
        EasingStyle = "Sine",
        EasingDirection = "InOut"
    }
)

vs

Roblox Tweens

local TweenService = game:GetService("TweenService")

local tweenInfo = TweenInfo.new(
    5,
    Enum.EasingStyle.Sine,
    Enum.EasingDirection.Out
)

local tween = TweenService:Create(workspace.Part, tweenInfo, {
    Position = Vector3.new(0, 10, 0),
    Color = Color3.fromRGB(255, 255, 0)
})

tween:Play()

What’s supposed to make it better?

Tween+ syntax is more readable, and you can provide TweenInfo in any order you want.

With TweenService you have to provide TweenInfo in the exact order which it tells you…
With Tween+ you can provide in any order and there’s autocompletion.

A case where TweenService would look ugly is if you didn’t want to provide all TweenInfo:

TweenInfo.new(2, nil, nil, 3, nil, 1)

Where as with Tween+ it would look like this:

{
	Time = 2,
	RepeatCount = 3,
	DelayTime = 1
}

Additionally, Tween+ gets rid of ugly enums and uses strings instead, while preserving autocompletion.

Did you forget to publicize the videos?

Yes, I will have to reupload them publicly, so give me a second.

I’m using Opera GX, i see videos now. Is there images aswell?

Are the videos visible to you now?

Most Developers would use a ModuleScript to get Tween Functions ready for their UI to keep it constant, also you’d rather not have the freedom of putting in strings I’d say, 1 typo and your tween won’t work. Imagine doing this in a global module, obviously it should be tested beforehand, but Enums are logical to use as a DataType to prevent mistakes.

Not sure if you have but I’d recommend the possibility of using the Enums themselves too if you can’t already.

Aside from that, for Developers to actually use your Module I would recommend doing it for things that are more valuable, look at for example ProfileService/ProfileStore, Maid and Admin Modules, those may be very useful. This on the other hand is just “learning” a new module to not follow Roblox’s Guidelines on how they’ve implemented it. Which in my eyes, is not a useful module. That’s like making your own custom Integer DataType instead of using the default. :sweat_smile:

What do you mean, aren’t videos enough?

There’s autocompletion, so it’s extremely hard to mess up.

Sorry, when i last checked there was a blank space above the videos as if there was another attachment.

They’re visible, thanks for re-publicizing!

2 Likes

The thing is, you’re looking completely besides the point.
There are many more differences between Tween+ and TweenService, that makes Tween+ significantly better. I’d recommend reading the post.

1 Like

I cannot see them either. I am on Safari.

It might be your browser not supporting AV1.
The previous issue was just that the videos were private, which is now fixed and most users are able to see them.

1 Like

Any performance changes? What about replication and server handling?

Creation is much faster, but unfortunately starting playback of tweens is slower because of all of the additional features it provides.

I haven’t benchmarked CPU usage when tweening, but I can assure you that it has been optimized.
Feel free to benchmark yourself, and don’t forget to share it if you do so!

Also, the UpdateEvent feature allows for significant performance improvements in certain scenarios. Of course TweenService does not have such a feature.

I thought about this, but this module’s purpose might not quite be that.
I want it to be usable for anybody and in any environment — it’s a replacement for TweenService.

1 Like

Okay, actually I was making a similar system for animations from keyframeSequence, so I know a little bit about this topic.

Personally, I love it when people write me a lot of constructive criticism. so now I’m going to ruthlessly evaluate your code :slight_smile:

  • Well, reading your code, I still involuntarily think that there are advantages here too, so let’s write down why your code is not bad:

The documentation is right in the code before the functions, so we see hints, not bad.
Typing is definitely very good, but with the caveats that it was worth putting them in a separate module. They were made purely for “beauty” (hints), although it was possible to work hard and do it in --!strict mode. Yes, it’s difficult for not the most experienced, but still
Also, I note that the code is well organized. Although I just poke my finger into any part of the code and immediately see how to improve readability, and so on, it’s still a difficult job, so I forgive
using your own SignalPlus is good! Judging by the code, they are faster than BindableEvent.

  • What I’m not sure about
    UpdateEvent: Providing a choice between PostSimulation, prestimulation, and PreRender for UpdateEvent gives flexibility, but it can also lead to problems. I’m not sure if it’s worth giving this choice, or if it’s better to limit yourself to one, the most appropriate option.
    table.freeze also seems to be meaningless in your code. No one in an adequate state will change a table from another code. Well, I’m not at liberty to say whether it’s bad or not.
    The native directive looks meaningless in untyped code. If the code had been typed correctly, --!strict would have been enabled, which would have added 20-30% of the code speed, but here the typing was done so that there would be no eye gouging, so this is not only pointless, but even a small anti-pattern is possible.

minuses:
The main problem: Why? The most important question that arises when viewing this code is: why create your own TweenService system? TweenService is an optimized, reliable, and supported Roblox component that provides a wide range of animation features. If there are no very good reasons to create your own system, then it is better to use TweenService. I mean, unlike your code, it’s much more type-safe, probably more convenient, and so on. Just create a wrapper for it that adds functionality.
The propertyExists function uses pcall, which is quite expensive. It is better to use smg like “offline API” and check OflflineAPI[object][property] == nil (or API[object][property] if u can)
The code is quite complex to understand and debug.
I didn’t notice any logic processing when we try to run two twins of the same property for an object. it would be nice to have a weigh system
I also didn’t notice the logic processing when the instance was destroyed. It will most likely give an error, but I think many people would like to be warned so that their myservergamainbestsuperpuper does not turn off due to a destroyed DataStorePartAttributetagImACollectionService (just joking)
also, the structure is not very good in my opinion.
In the current implementation, each tween creates its own separate connection to the RunService.Prestimulation (or another selected UpdateEvent). This can lead to significant overhead costs, especially with a large number of simultaneously running twins.
Ideally, we strive to ensure that all active tweens are handled within a single connection to the RunService.PreSimulation. This will significantly reduce the load on the CPU and improve performance.

Consider using a centralized scheduler that will iterate through all active tines and update their values within a single RunService cycle.PreSimulation. This will require a more complex architecture, but will eventually lead to more efficient operation.
instanceTweens using Instance as a key is a problem. Use Weak Tables.
Or use a unique ID, although I find it pointless.

looked into easing WTHELL MAN THERE ARE SO MANY REPETITIVE LINES OF CODE! “Magic numbers” are bad

(Im used google translator, Im sorry if my text is “unreadable”)

AW:
and I would also say that instead of string … string, you should definitely do `{string1} {string2}(``), except for the moments with tables.
There’s a lot of duplicate code here
To configure it, consider the builder pattern, for example, in roblox it is a separate “TweenInfo” object