Animates numbers, entity positions and alpha over time, with easing curves, repeats and sequences.
Import blitz.tweening As T gives your program these, with T. in front. Each name leads to its part of the reference.
CreateTweens:TweenGroup()TweenValue:Tween(group:TweenGroup, target:ValueTarget, destination:Double, seconds:Double, options:TweenOptions)TweenMove:Tween(group:TweenGroup, entity:Int, x:Float, y:Float, z:Float, seconds:Double, options:TweenOptions)TweenAlpha:Tween(group:TweenGroup, entity:Int, initialAlpha:Float, alpha:Float, seconds:Double, options:TweenOptions)AfterTween:Int(tween:Tween, predecessor:Tween, delay:Double = 0D)TweenState:Int(tween:Tween)PauseTweens:Int(group:TweenGroup, paused:Int = True)TweenEvents(group:TweenGroup, enabled:Int = True)CancelTween:Int(tween:Tween, finish:Int = False)UpdateTweens:Int(group:TweenGroup, seconds:Double)NextEvent:TweenEvent(group:TweenGroup)Value Implements ValueTargetvalue:DoubleGetValue:Double(), SetValue:Int(value:Double)TweenOptionseasing:Int = Ease.Linear, delay:Double, repeatCount:Int, pingPong:IntTweenEventID:Long(), State:Int()TweenID:Long()TweenGroupValueTargetGetValue:Double(), SetValue:Int(value:Double)EaseLinear, Smooth, QuadIn, QuadOut, QuadInOut, BackOut, BounceOutStateWaiting, Running, Completed, CancelledInfinite:Int = -1Tweening makes things change smoothly. Instead of snapping a door open, or jumping a score straight from 0 to 500, you say where something should end up and how many seconds it should take, and the module works out every frame in between. Things can speed up and slow down gently, overshoot and settle, bounce, repeat, go there and back, or move one after another.

Open race.bb and run it. Seven balls race to the right and back again, for ever, each with a different easing curve. Watch how each one sets off, slows down and arrives.
; Seven balls race across the screen and back, each with its own easing curve.
Dialect "modern"
Import blitz.tweening As T
Type Racer
Field ball:Int
Field name:Str
End Type
Graphics3D 800, 480, 0, 2
SetBuffer BackBuffer()
Local camera:Int = CreateCamera()
CameraClsColor camera, 18, 52, 79
CameraZoom camera, 1.6
PositionEntity camera, 0, 0, -13
Local light:Int = CreateLight()
RotateEntity light, 40, 30, 0
; One group runs all the tweens. The options say how each tween moves.
Local group:T.TweenGroup = T.CreateTweens()
Local options:T.TweenOptions = New T.TweenOptions
options.repeatCount = T.Infinite ; Keep going for ever...
options.pingPong = True ; ...there and back again.
Local curve:Int
For curve = T.Ease.Linear To T.Ease.BounceOut
Local racer:Racer = New Racer
Local name:Str
Read name
racer.name = name
racer.ball = CreateSphere(16)
ScaleEntity racer.ball, 0.4, 0.4, 0.4
EntityColor racer.ball, 255, 120 + curve * 20, 60
PositionEntity racer.ball, -3, 3 - curve, 0
; Move to x = 6 in two seconds, with this curve.
options.easing = curve
T.TweenMove group, racer.ball, 6, 3 - curve, 0, 2D, options
Next
Delete options ; Each tween keeps its own copy.
Local previous:Int = MilliSecs()
While Not KeyDown(1)
; Tell the group how many seconds have passed, and it moves every ball.
Local now:Int = MilliSecs()
T.UpdateTweens group, Double(now - previous) / 1000D
previous = now
RenderWorld
For racer = Each Racer
CameraProject camera, -5.2, EntityY(racer.ball), 0
Text ProjectedX(), ProjectedY(), racer.name, True, True
Next
Flip
Wend
Delete group
End
Data "Linear", "Smooth", "QuadIn", "QuadOut", "QuadInOut", "BackOut", "BounceOut"
The program makes one tween for each ball, and then, every frame, tells the tween group how much time has passed. The group does the rest. The module never looks at the clock itself: it only knows the seconds you give it, so you are in charge of pausing, slow motion and fast-forward.
Make both moves, then tell the second one to start after the first.
AfterTween takes the tween that waits, the one it waits for, and how many
seconds to pause in between.
Local opening:T.Tween = T.TweenMove(group, door, 2.4, 0, 0, 0.8D, Null) Local closing:T.Tween = T.TweenMove(group, door, 0, 0, 0, 0.8D, Null) T.AfterTween closing, opening, 0.5D
Null in place of options means "the usual way": a straight, steady move.
Set delay in the options. A tween copies its options, so you can delete them
straight away, or change them and use them again.
Local options:T.TweenOptions = New T.TweenOptions options.delay = 1.5D ; Wait a second and a half first. options.easing = T.Ease.QuadOut T.TweenMove group, ship, 0, 10, 0, 3D, options Delete options
Pause the group. Its tweens stay where they are until you carry on.
T.PauseTweens group, True ; Pause. T.PauseTweens group, False ; Carry on.
Give things that should keep moving while the game is paused, such as a menu, a group of their own.
Tween a Value instead of an entity, and draw its value.
Local score:T.Value = New T.Value T.TweenValue group, score, 500D, 1D, Null ; Count up to 500 in a second. ; Then, each frame, after T.UpdateTweens: Text 10, 10, "Score: " + Int(score.value)
T.TweenAlpha group, ghost, 1, 0, 2D, Null ; From solid to gone in two seconds.
You give the alpha to start from, because Blitz3D+ has no command to read an entity's alpha back.
Ask for its state.
If T.TweenState(opening) = T.State.Completed Then Print "The door is open"
CancelTween stops it where it is. Add True to jump to the end instead.
T.CancelTween opening ; Stop where it is. T.CancelTween opening, True ; Jump to the end.
Blip's tip
Delete the tween group before you free the entities it moves. Deleting the group deletes all of its tweens too, so you never need to delete them one by one.
TweenMove moves an entity as
PositionEntity does without its global flag, starting from wherever the
entity is when the tween starts.D, as in 0.8D.T.NextEvent. If your game never reads them, switch them off with
T.TweenEvents group, False.Three more complete programs come with the module:
AfterTween.The reference below has every function, option and rule.
Animate values, parent-local positions and alpha with explicit seconds, seven
curves, repeats and branching sequences. This is an unpublished 0.1.0 preview
for Extended with object-lifecycle-v1. It has no package dependencies, import
side effects, binary assets or package scripts.
Blitz3D+ comes with this module, so any Extended program can
Import blitz.tweening. Open an example and run it.
Dialect "modern"
Import blitz.tweening As T
Local group:T.TweenGroup = T.CreateTweens()
Local score:T.Value = New T.Value
Local tween:T.Tween = T.TweenValue(group, score, 100D, 1D, Null)
T.UpdateTweens group, 0.25D
Print score.value ; 25
T.UpdateTweens group, 0.75D
Print T.TweenState(tween) ; T.State.Completed
Local event:T.TweenEvent = T.NextEvent(group)
If event <> Null
Print event.ID()
Delete event
EndIf
Delete group ; Deletes tween; never deletes score.
Delete score
End
The caller supplies time; the library never polls clocks/input, sleeps, draws or
updates the world. Use one group per independently paused/time-scaled system.
Normal game order is input, decisions, UpdateTweens, event processing, world
and UI drawing. All calls belong on the main thread. Worker execution is deferred.
| File | What it does | Controls |
|---|---|---|
examples/score.bb | Quick start: an eased numeric score and generated HUD | Space earns 100; P pauses; C cancels; F finishes; R resets; Escape exits |
examples/door.bb | Ordinary integration: a sliding, parented door opens, waits and closes | Space restarts; P pauses; C cancels either phase; Escape exits |
examples/fade.bb | Advanced: interrupted fades coexist with ping-pong movement; IDs/state events and ownership | Space reverses (also while paused); P pauses; C cancels at destination; R resets; Escape exits |
Each example's --verify mode executes finite scripted interactions through the
same control paths, checks results, draws and tears down. Normal runs use measured
seconds, clamped to 0.1 per frame as a game policy; verification uses 1/30 s.
Every mesh, colour, bar and label is generated in source.
CreateTweens():TweenGroup creates a caller-owned scheduler. Configuration is a
caller-owned TweenOptions; factories copy it, and explicit Null means defaults.
The compiler does not support object-valued Null default arguments.
Local options:T.TweenOptions = New T.TweenOptions options.easing = T.Ease.Smooth options.delay = 0.25D options.repeatCount = 1 options.pingPong = True Local tween:T.Tween = T.TweenValue(group, score, 100D, 1D, options) Delete options ; The tween already copied it.
| Operation | Contract |
|---|---|
TweenValue(group, target, destination:Double, seconds:Double, options):Tween | Borrows ValueTarget; captures when starting, after delay/dependency. Multiple scalar writers are allowed; cancel old ones explicitly. |
TweenMove(group, entity:Int, x:Float, y:Float, z:Float, seconds:Double, options):Tween | Borrows entity; captures parent-local XYZ when starting. Replaces this group's active movement on that entity. |
TweenAlpha(group, entity:Int, initialAlpha:Float, alpha:Float, seconds:Double, options):Tween | No runtime alpha getter exists. Supply the starting alpha. A running fade replaced in this group supplies its last written alpha instead. Inputs and output clamp to 0..1. |
AfterTween(child, predecessor, delay:Double=0D):Int | Replaces a same-group dependency and its extra delay; adds that delay to the child's options delay. An already completed predecessor counts as finishing now, so the child still waits its delays and runs its full duration. Returns False for a cycle, a started/terminal child or an infinite predecessor. Wrong group/invalid seconds is an error. |
UpdateTweens(group, seconds:Double) | Samples one fixed horizon in dependency order, with the bounded-work rules below. |
PauseTweens(group, paused:Int=True) | Paused updates discard their incoming delta. Existing work/time remains retained. |
CancelTween(tween, finish:Int=False) | Terminal cancellation event; leaves current value, or writes the literal destination once. Does not delete the handle. |
TweenState(tween):Int | State.Waiting, Running, Completed or Cancelled; no event consumption. |
tween.ID():Long | Monotonically increasing, non-reused identity within its group. |
TweenEvents(group, enabled:Int=True) | False stops queuing terminal events and discards queued ones, so fire-and-forget games that never poll cannot reach the event capacity. Not allowed inside callbacks. |
NextEvent(group):TweenEvent | Transfers the oldest event, or Null. event.ID() / event.State() are immutable. Caller deletes the event. |
Procedure-style calls and ValueTarget.SetValue have the compiler's Int result
convention; ignore that result. Only AfterTween's result is a success status.
GetValue replaces the plan's illegal Read spelling (Read is reserved).
Value is a simple caller-filled record with value:Double. For game-specific
values, implement the small interface; there is no arbitrary callback dispatcher:
Type DisplayScore Implements T.ValueTarget
Field shown:Double
Method GetValue:Double()
Return Self.shown
End Method
Method SetValue:Int(value:Double)
Self.shown = value
End Method
End Type
Options default to Ease.Linear, zero delay/repeats and no ping-pong.
repeatCount counts extra legs, and Infinite=-1 repeats forever.
Ping-pong reverses odd legs: one repeat finishes at the captured initial value.
Finite zero-duration repetition collapses to one destination write and completion,
even with ping-pong. Infinite zero-duration repetition is invalid. Non-ping-pong
repeats reset to their starting value at each leg boundary; ping-pong is continuous.
Curves are Linear, Smooth (cubic smoothstep), QuadIn, QuadOut, QuadInOut,
BackOut and BounceOut. Progress is bounded to 0..1 and endpoints are exact.
BackOut intentionally overshoots; alpha still clamps. BackOut interpolation that
exceeds finite Double or engine Float range raises an error before that write.
All times/deltas must be finite, non-negative Double seconds. Zero delta is useful for starting a ready tween or continuing budgeted work. Values/coordinates must be finite. A deleted borrowed target is rejected; engine handles have no general validity query, so entities must outlive their tweens.
Progress is derived from captured endpoints and elapsed time, never from an incrementally interpolated value. Repeats use floating remainder to avoid an infinite quotient for tiny durations and a huge elapsed time. Exact endpoints, equal extremes and convex interpolation of opposite extremes stay finite. Overflowing schedules and positive times too small to advance their base clock are errors, not silently lost time. Ordinary binary64 rounding still applies.
Dependencies carry excess time across finite durations and delays, including branches and zero-duration chains. Capture happens when a ready tween is first visited. Ordering is deterministic: registration order with each ancestor visited before its descendants. This is update-resolution capture, not chronological substepping of every property transition inside a large delta.
For example, an X move 0→10 over 2 s and a second X move to 20 over 1 s delayed 0.5 s give X=7.8125 after a single 0.75 s update. Updating 0.5 then 0.25 s gives X=6.875, because the second tween captured earlier. Both are intentional and tested. Fixed-capture independent tweens are partition-invariant within rounding; overlapping property writes and game/callback changes depend on update boundaries.
Movement and alpha are separate properties. Replacement coordinates only the same property in the same group, at the instant its new tween starts. Completed fades no longer supply alpha; pass the known current alpha after completion, cancellation, deletion or game-owned changes. Other groups/game writes are uncoordinated. Cancelling with finish writes the literal destination even for a waiting tween or a ping-pong tween. Repeating a terminal cancellation is a no-op.
A round snapshots the group and a time horizon. Each UpdateTweens evaluates at
most 65,536 records. If the round needs another call, it retains its cursor and
horizon; incoming deltas accumulate for the next round. Use UpdateTweens(group,0D)
to continue without adding time. New tweens created during a round use its horizon
as their epoch and start no earlier than the next round. Snapshot/preflight and
cancellation propagation still scan the group: the budget bounds evaluated target
records, not all CPU operations. Terminal handles also occupy snapshot entries;
delete them when the game no longer needs them.
During GetValue or SetValue:
GetValue.AfterTween can fence and rewire a
still-waiting tween; effective pending edges participate in cycle rejection.
Edges commit between whole rounds, including when a round spans calls.CancelTween fences further evaluation. State/events and optional destination
writes occur after that call's evaluation. Repeated requests coalesce;
finish=True cannot be downgraded. Completion loses to a cancellation requested
by its final write. A tween already made terminal by property replacement
does not later execute an older pending finish request.One terminal-event slot is reserved for every nonterminal tween at creation.
queued events + staged events + nonterminal tweens <= 262144 is maintained even
when callbacks create or cancel work. Creation fails before registration when
no slot remains. This prevents capacity errors halfway through completion writes.
Drain events regularly and delete unused active handles, or switch events off
with TweenEvents group, False when the game never polls them. The preflight covers
capacity, not system allocation failure or arbitrary callback exceptions; runtime
errors terminate the program and do not promise rollback of earlier writes.
Sorting a changed dependency forest is O(n + edges), using an explicit stack.
AfterTween checks O(ancestor depth); ordinary rounds and unregister are O(n).
Completed or cancelled handles that you keep are skipped by rounds, so they do not
use the 65,536 evaluations each update allows, though they still hold memory until deleted.
| Resource | Owner and deletion rule |
|---|---|
| TweenGroup | Caller; delete before targets/entities. Owns all remaining handles and undelivered events/containers. |
| Tween | Group; caller may delete a child outside callbacks. Unregisters itself. Terminal handles remain queryable until deletion. |
| Value/custom target/entity | Borrowed, never deleted by group/tween. Caller releases it after its groups. |
| TweenOptions | Caller; copied immediately, may be deleted after creation. |
| NextEvent result | Ownership transfers to caller. Contains IDs/state, no references to the group/handle. Survives both. |
Delete tween leaves the current value, makes no target calls and emits no event.
Deleting an unfinished predecessor silently cancels all waiting descendants,
including branches/pending edges. Deleting a completed predecessor preserves its
completion time for waiting dependants. Delete group detaches then deletes
remaining children and all owned containers/events without callbacks. No global
Delete Each or shutdown sweep is required. Typed Null/already deleted aliases
are harmless to Delete; other API use rejects deleted handles.
Pause, cancellation and disposal are distinct actions. There is no separate Stop alias: use pause to resume later, cancellation to end and notify, deletion to release ownership. There is no persistent state transaction, so Save/Commit are not APIs here; destruction never saves or commits game work implicitly.
Examples of programming errors:
; T.UpdateTweens group, -1D ; invalid seconds ; Local x:T.Tween = New T.Tween ; private constructor; use a factory ; Print tween.duration ; private representation ; Delete group inside SetValue ; callback deletion is forbidden
Separate timelines, a parallel-group DSL, more easing families, skeletal animation and worker adapters remain explicitly deferred.