Skip to content

v1.7.0 — Tweens on one shared loop

Back to all updates
v1.7.0August 24, 2026

Twill’s own Tween module moves every value in the game on one connection.

Tween is a table rather than an Instance. Every tween in the game advances on one connection, which exists only while something is playing, so a place that tweens nothing pays nothing.

Twill.Tween.Play(gui, { Position = UDim2.fromScale(0.5, 0.5) }, { Time = 0.4 })

Tween properties, attributes, pivots, scales and tables

Section titled “Tween properties, attributes, pivots, scales and tables”

Besides properties, Tween moves attributes, a model’s pivot, a model’s scale, and the fields of a plain table.

Twill.Tween.Play(model, { Pivot = openCFrame, Scale = 1.2 }, { Time = 1 })
Twill.Tween.Play(part, { ["@Charge"] = 100 }, { Time = 3 })

A tween of a plain table needs no DataModel, so it can move a number that nothing on screen owns yet, and it can be tested on its own.

A destination given as an array is a curve. One control point makes a quadratic Bézier and two make a cubic. Vector2, Vector3, UDim2 and CFrame values can be curved.

Twill.Tween.Play(part, {
Position = { finish, controlOne, controlTwo },
}, { Time = 1.5 })

A Color3 is interpolated through Oklab, so blue moving to yellow passes through green rather than grey.

Starting a tween takes over every property it moves and cancels the older tween, so two tweens never write the same field.

slow:Play()
fast:Play() -- slow is cancelled, and Position now belongs to fast

Ownership is per property and per target, so tweens moving different fields of one instance do not disturb each other.

Tweening an Instance on a server sends every frame to every client, so it is refused unless you pass AllowServer:

Twill.Tween.Play(part, { Transparency = 1 }, { AllowServer = true })

A write can fail long after a tween starts, for example when a table is frozen or an instance is replaced. The failing tween now stops and reports faulted through Stopped, and every other tween keeps running.

  • Refusal messages across every module follow one shape: no trailing full stop, no bracketed module prefix, and the function named as Module.Function.

    before: [Twill.Net] nothing has declared 'X' yet
    after: Net.Get has no remote named 'X' yet
  • Scope, BigNumber, Format, Replication, Data, Random, Monetization and Leaderstats refuse arguments of the wrong type. Before, Scope.Player given something other than a player returned the wrong bag.

Every change in this release is listed in the changelog.