SyncTween📡 - Rich TweenService Replication Module

SyncTween

Created by @creaco - November 8th, 2024

Licensed under the GNU GPLv3 License

SyncTween is a tweening library designed to help server-to-client animations, with several features that other modules do not offer.


Roblox Wally Button Standard Repository

Untitledvideo-MadewithClipchamp2-ezgif.com-video-to-gif-converter


If you find the module useful and want to support my work, you can make a small donation directly in the game. It is greatly appreciated!


Links

  • More information on TweenService found here.
  • If you are unfamiliar with how replication works, please look at this DevForum post.
  • This project was inspired by TweenService2, although not built onto it.

Advantages

  1. Lower Server Load
    By handling animations on the client side, SyncTween reduces the processing burden on the server.

  2. Smoother animations
    Compared to the server, the client runs at a faster clock, meaning the animations will not be “choppy”.

Key Features

  1. Syncing Animations
    SyncTween allows animations to be synchronized across all clients. Here’s the definition of “synchronization”:

    • Synchronization ensures that all clients see the animations in the same state at the same time.
    • By default, all tweens will end at the same time, this means it compensates for server lag.
  2. Custom Animations
    You can define custom animations on the client side. Check out the Custom module for examples.
    You can also use other Tweening modules combined with this module using a custom animation.

  3. Support for Streaming Enabled & Streaming Out
    SyncTween works with Streaming Enabled and Streaming Out via CollectionService.

  4. Selective Replication
    Make animations visible to selected clients without replicating them to the server. For example, hide a door for User X but keep it visible for others.

  5. Selective Framerate
    Make certain animations run at a lower framerate to decrease the client load.

Current Limitations

  1. Custom animations cannot be paused.

How to Use SyncTween

To construct a SyncTween, use the following syntax:

SyncTween.new(
    object: Instance,                                       -- The object you want to animate.
    tweenInfo: (TweenInfo | TweenArray | string)?,          -- TweenInfo of the animation.
    properties: { [string]: any }?,                         -- Properties you want to animate.
    update: boolean?,                                       -- Should the animation be updated to the server?
    sync: (boolean | number)?,                              -- Synchronize all clients / set framerate.
    additional: { any }?,                                   -- Additional data for custom animations.
    uuid: string?                                           -- UUID of the animation (optional).
)

Parameter Notes:

  • tweenInfo: Accepts a TweenInfo, a TweenArray, or a string.
    • If a string is provided, it will be treated as a custom animation.
    • A TweenArray looks like:
      { Time = 1, EasingStyle = Enum.EasingStyle.Linear, EasingDirection = Enum.EasingDirection.InOut }
      
    • It’s recommended to use TweenInfo to avoid confusion, the change was necessary for replication purposes.

Examples:

  1. Animate a part’s position:

    SyncTween.new(workspace.Part, TweenInfo.new(1), { Position = Vector3.new(0, 10, 0) }):Play()
    

    This will animate the part to the position (0, 10, 0) over 1 second.

  2. Animate a color for a specific player:

    SyncTween.new(workspace.Part, nil, { Color = Color3.new(1, 0, 0) }, false):Play({Player1})
    

    This will turn the part red only for Player1.

  3. Play a custom “Rainbow” animation at 10 FPS:

    SyncTween.new(workspace.Part.Highlight, "Rainbow", true, 10):Play()
    

Global Methods

  • SyncTween.get(object: Instance, player: Player): { Sync }
    Retrieve all animations currently playing on the specified object.
    On the client, this will return everything that is playing, on the server, it will return only Syncs that the player can view.

SyncTween Class Methods

  • Play(players: { Player }?)
    Start the animation.
    Ability to set the scope on who receives the animation.

  • Pause(delta: number?)
    Pause the animation.
    (Does not apply to custom animations)

  • Cancel()
    Cancel the animation.


Summary

You can create a SyncTween (or “Sync”) using SyncTween.new(). This class inherits the usual tween methods (:Play(), :Pause(), :Cancel()) while offering additional configuration options, such as:

  • tweenInfo
  • update
  • sync
  • uuid

This module is great for having animations replicate to various clients without the downside of server lag.
This module also supports settings like framerate, which gives more freedom on how expensive animations should be.

The module is written in --!strict mode, and the Sync type is exported for use.


If you have any questions regarding the module, feel free to drop comments below this thread. I will do my best to get back as soon as possible. If there any issue with the module feel free to let me know here. Thank you!

41 Likes

I know, from looking at other threads, that Synchronization can be a hard subject to grasp. To combat this and to hopefully clear up any confusion, I will try to explain the method further in depth.

This module offers 2 main ways to animate your Tweens:

  1. Synchronized (Same state across clients)
  2. Unsynchronized (State varies across clients)

They both end at the same time, however the way they get to the end is different.
To better explain, I have made a visual comparison:

Both Synchronized and Unsynchronized account for server lag. Where they differ is how they handle this delay

  1. Synchronized prefers to jump in time to be sure that the current state is the same across clients, this means that it may visually jump at the start.
  2. Unsynchronized prefers to speed up the animation by removing the delay, this means that it may visually appear faster.

Hope this clears up any confusion!

3 Likes

Really cool resource. Is there a Wally package I can use?

For server replication, it currently waits for the tweensync to be finished and then the end result will get replicated on the server.

I’m trying to make a moving laser part where it also detects players and damages them with this, is there a way I can do that? if not, do you have an idea? note: I don’t wanna do damage detection on client though. so basically, server sided, but smooth animation on the client.

Hello Yiannis123, I have updated the post with the Wally link, thank you for your suggestion.

1 Like

If you plan on moving a laser and want to do server checks I advise you to simply use TweenService in this case. Unless (like you said), you make the verification on the client.

Reason being this module avoids updates on the server to avoid lag, so on the server the laser would not be moving.

Best,
Creaco.

basically im using this to tween the players walkspeed when running, just to make it seem more like a speedup instead of an immediate speed change, but for example when the player starts running then stops running before the first tween finishes, the tweens overlap and sometimes when the player isnt running, the walkspeed is still kept as the running speed, any help?

1 Like

(Sorry for duplicate comment, DevForum bugged out)

I have tried recreating the issue you mentionned, but I was unable to recreate the issue.
Could you please provide a file to recreate it?

Meanwhile, I have just sent a patch for an issue regarding overlapping synchronized animations, this may have been your problem, let me know if it persists.
I have also included one more example inside of the test place.

I really like this mODULE, i have a suggestion, you could make a additional tween?, let’s say, i want to change the position and rotation of a object at the same time, like , i want it to move but also rotate, you can make a additional function for this, because if i do this even on a normal tween or right now, the rotation will overwrite the position and viceversa, making it buggy

2 Likes

Great catch!

I have updated the module once again to take this into consideration. If you come across any other issues concerning this please let me know.

1 Like

Version 1.0.4 is out, fixed some bugs concerning Pausing and should now work as intended.

:Cancel() -- does not take any arguments anymore 
:Pause(delta: number?) -- now takes a delta argument (which is completely optional)
1 Like

Hello! Is there documentation for the custom animations module. I am not familiar with it.

Been using your module SyncTween, and found some issues with it:

  1. When sync and replicating at the same time, but it will work and complete, but no animation is preset;
    Property CFrame is not valid for class UnionOperation. (x30) - Client - Helper:73

Code Example:

local door1 = doorObject.Door1.PrimaryPart
local defaultTweenInfo = TweenInfo.new(1.5, Enum.EasingStyle.Sine)

tween = SyncTween.new(door1, defaultTweenInfo, {CFrame = getRotation(door1, toggleState)}, true, true)
tween:Play()
tween.Completed:Wait()
print("tween completed")

Hello, I hope you are doing well, sorry for the late response, was busy with school!

Currently the custom animation supports something like this:

Custom.Rainbow = {
	-- Example for an animated Highlight.
	clk = function(signal, object, original, properties, additional, totalDelta, FPS)
		local object: Highlight = (object :: Highlight)

		local callback = function(delta: number)
			local hue = delta % 1
			object.FillColor = Color3.fromHSV(hue, 1, 1)
		end

		return bindCallbackToSteppedWithFPS(callback, FPS, totalDelta)
	end,

	clear = function(connection: RBXScriptConnection)
		connection:Disconnect()
	end,

	orig = function(object, properties, original, additional)
		local object: Highlight = (object :: Highlight)

		return {FillColor = object.FillColor}
	end,
}

You are able to use this custom animation by doing:

local Animation = SyncTween.new(script.Parent.Highlight, "Rainbow")
Animation:Play()

Your custom animation needs three fields:

  1. A “clk” function, where the following arguments are passed:
signal, object, original, properties, additional, totalDelta, FPS
  • You are able to manually fire the “signal” by doing signal:Fire(), this will indicate that the animation is finished to the client (to clean up)
  • You are able to send extra information through the “properties” field.
  • Whatever you return inside the “clk” function will be stored as a temporary value that will be passed onto the “clr” function when the animation is over.

  1. A “clr” function, where it only takes care of cleaning up any connections you may have returned inside the “clk” function.

  1. The “orig” function is simply to get specific original properties incase you do cancel the animation on the server, it will set these properties onto the instance.

All in all the custom area of this module can be quite tricky, and is mostly useful for things that are infinitely repeating, or infinitely ongoing. At least thats what the module offers at the moment.

Hey, could you hit me up with a DM so I can take a closer look? I can’t seem to replicate the issue. Thanks :slight_smile:

SyncTween Version 1.0.5 has been published :tada:


Changes:

  1. Fixed an issue where a lot of animations would take longer to load. Now uses multithreading to load them to prevent delays.
  2. Fixed unpredictable behaviour when having replicate = true on the client.
  3. Added some more docs inside the “Custom” Module to clearly explain how to create custom implementation, and also talks about limitations.
  4. :warning: (To those who have added custom implementations) “clk” has been renamed to “run”, this is to clarify that the function is not running on every clk cycle.

There is no security updates, it is purely a performance and a small bug fix :slight_smile:

(Minor update)

Version 1.0.7 (w/ 1.0.6) fixed 2 issues:

  1. Some errors would be thrown in the console if the object isn’t loaded in yet.
  2. The Wally package has been fixed by adding the default.project.json file. Instead of importing the whole folder, it should now only import the Module!
    :dizzy:

(Minor update)

Version 1.0.8 brings clarity changes:

  1. Variable “replicate” has been renamed to “update” after realizing some confusion with certain users.
  2. Type for “Additional” has been widened to " {any} ". This change was due because some developers pointed out that sending " { [string]: any } " may not always be necessary. Custom examples have been updated to show how to properly type check for these cases, however nothing should break from this update.
    :maple_leaf: