TweenService.luau: Pure Luau TweenService Reimplementation

TweenService as it is is pretty mediocre. Its API is difficult to learn, and its Instance-based functionality is often very limiting. I am humble to announce that I have solved neither of those issues, and have instead completely rewritten TweenService to be almost exactly the same as the original. I did this because I noticed that every time I saw a module that made its own TweenService implementation, all the EasingStyles were hard-coded. I’m assuming this is because they copied Robert Penner’s Easing Functions, which for some inexplicable reason are all hard-coded to have an easing function for each combination of EasingStyle and EasingDirection, and are also likely to be the functions Roblox copy-pasted into TweenService. My hope is that TweenService.luau will help someone debug their custom tweening solution, because God knows how many times I’ve had to make a mini-implementation of my own for simple things that the vanilla TweenService should be capable of.

Download it here: https://create.roblox.com/store/asset/17740293905/TweenServiceluau

For anyone interested in some technical information about how the easing works, I’ll share. Each EasingStyle is associated with a function which takes a value between 0 and 1 as an argument to represent percentage completion of the tween, and returns a value between 0 and 1 to represent how close tweened properties should be to their goals at the provided percent completion. Here’s the function for EasingStyle.Quad:

function quad(completion)
    return completion ^ 2
end

EasingDirections are also associated with a function. They are glorified decorators for EasingStyles. They take the same value between 0 and 1 as an argument to represent percentage completion of the tween, and an EasingStyle function as a second argument. Here are the In and Out EasingDirections:

function in(completion, style)
    return style(completion) --in is the default direction for every style... except bounce for some reason
end
function out(completion, style)
    return 1 - style(1 - completion)
end

Known Issues

TweenService can tween EnumItems and TweenService.luau cannot. Forgot to add support for it, but if you do happen to be tweening EnumItems, feel free to send me a postcard from Stupid Town.

All of the EasingStyles and EasingDirections are included in the source code of the module, including an OutIn EasingDirection, which is just for fun. The easing functions are mostly from Robert Penner. I might have also taken some from BoatTween. Don’t remember. None of the functions are 100% accurate to the original TweenService; most are accurate to two decimal places. I imagine the reason why probably has something to do with floating point precision: Luau uses double-precision floats and I guess TweenService uses single-precision.

5 Likes

I’m confused what the difference between this and TweenService:GetValue is, can you clarify if there is a difference?

3 Likes

Very cool module, will make sure to read how it’s put together and see how it performs internally.


Unrelated question,

That’s new on my list (can tween enums but not NumberSequences). What I don’t get is how and why you would even do it. Like, smoothly change a camera’s CameraType? Or what is this usable for? How is it even achieved, by incrementing an internal index and seeing what Enum is available for that number??

TweenService:GetValue is just a function of TweenService. TweenService.luau is literally TweenService rewritten in Luau without using any methods of the built-in TweenService. It has its own custom-made GetValue function that should act almost exactly the same as the built-in TweenService:GetValue. Being a TweenService rewrite, it also has a Create function that returns a custom Tween object that you can call Play, Pause, and Cancel on, read its PlaybackState, and listen for a Completed event

1 Like

Performance is going to be difficult to measure, at least if you compare it to the real TweenService, but I doubt its faster considering the fact that I never cache any __index calls, and the Luau API will fire changed events every frame unlike TweenService. APIs are exactly the same as the real TweenService, except instead of Enum.EasingStyle it would be TweenService.EasingStyle. Enum.EasingDirection also becomes TweenService.EasingDirection, and TweenInfo.new becomes TweenService.TweenInfo.new

As for how, each Enum is associated with an integer, so tween it like a normal number, except return rounded answers instead of floats. As for why, there is literally no reason to do it.

1 Like

So to be clear, there is no difference? (I understand that this is rewritten, but input into one returns the same output as the other if I’m reading this correctly)

I guess what I’m confused on is why using TweenService.luau is preferable over the built-in TweenService. Yes, I understand that having the code exposed is nice, but if this is doing the exact same thing as TweenService minus linking Tween behavior to a Tween instance (and it’s not clear why that’s an advantage), what’s the pro to using this?

I understand the answer to my own question might be that exposed code with identical behavior = an opportunity for additions/expansion of TweenService behavior while retaining same API controls - but that’s just my answer, not necessarily yours. You’re referencing a few vague critiques of TweenService, but I’m missing the specificity to actually pick-up what those critiques are so I can understand this as more than just a rewrite of TweenService with the same outputs as the vanilla engine service.

Ex:

What are these things that TweenService hasn’t been able to do that you are referencing here? Presumably, TweenService.luau can do these things, but what are they?