Tweeny 4.1.0
A Tweening library for modern C++
Loading...
Searching...
No Matches
Tweeny Manual

This document is the manual for Tweeny. It walks you through all the important steps when creating and controlling tweens.

Note
Coming from Tweeny 3.x? Check out the migration guide!

Creating Tweens

The Builder Pattern

Tweeny uses a fluent builder API to create tweens. The tweeny::from function returns a builder object, not a tween directly. You configure the interpolation using the builder's methods (to(), via(), during()) and then call build() to create the actual tween object.

// tweeny::from returns a builder
auto builder = tweeny::from(0);
// Configure the builder (methods modify the builder and return a reference)
builder.to(100).during(60U);
// Build the tween
auto tween = builder.build();
tweeny_builder< false, FirstValue, RemainingValues... > from(FirstValue firstComponent, RemainingValues... remainingComponents)
Creates a new tween builder starting from the specified value(s).
Definition tweeny.h:347

Most commonly, you'll chain all calls together in a single expression:

auto tween = tweeny::from(0).to(100).during(60U).build();
tweeny_builder< true, FirstValue, RemainingValues... > to(const FirstValue &firstComponent, const RemainingValues &... remainingComponents) &
Adds a target keyframe to the tween.
Definition tweeny.h:109
tweeny_builder & during(FrameCountsType... frame_counts)
Specifies per-component frame durations for the last keyframe segment.
Definition tweeny.h:245
Note
Important: Unlike Tweeny 3.x, you must explicitly call build() to create a tween. The builder can be reused and modified to create variations.
auto builder = tweeny::from(0).to(100).during(60U);
auto tween1 = builder.build(); // First tween (0→100 in 60 frames)
builder.to(200).during(120U); // Add another keyframe
auto tween2 = builder.build(); // New tween (0→100→200)

Once built, a tween's keyframes, durations, and easing functions are immutable. However, the tween's current state (frame position and value) changes as you navigate it with step(), seek(), or jump().

Value Types

Tweeny can interpolate single values, multiple values, or values of different types. The types you pass to tweeny::from determine the tween's type signature, which affects all subsequent builder methods and the tween's return values:

// Single value tween
auto t1 = tweeny
::from(0)
.to(100)
.during(60U)
.build();
// Multi-value tween (homogeneous)
auto t2 = tweeny
::from(0, 0, 0)
.to(255, 128, 64)
.during(60U)
.build();
// Multi-value heterogeneous tween
auto t3 = tweeny
::from(0, 'a', 1.0f)
.to(10, 'z', 5.0f)
.during(60U)
.build();

From and To

Every tween needs at least a starting point and an ending point. tweeny::from specifies the starting values, and you must call to() at least once to specify target values. This requirement is enforced at compile time - if you try to build a tween without calling to(), you'll get a compilation error.

// This won't compile - no to() called
// auto tween = tweeny::from(0).during(60U).build(); // ERROR!
// This is correct
auto tween = tweeny::from(0).to(100).during(60U).build();

The number and types of arguments to to() must match those passed to from():

// Single value: one argument
auto t1 = tweeny::from(0).to(100).during(60U).build();
// Two values: two arguments of matching types
auto t2 = tweeny::from(0, 'a').to(100, 'z').during(60U).build();
// Wrong number of arguments - won't compile
// auto t3 = tweeny::from(0, 0).to(100).build(); // ERROR!

Duration

Every interpolation segment needs a duration. The during() method specifies how many units (typically frames or milliseconds) the interpolation should take to reach the target values. The duration is always an unsigned 32-bit integer (uint32_t).

Unlike a missing to(), omitting during() is not a compile-time error. The segment’s duration defaults to 0, so both keyframes sit at the same frame: progress() is always 1.0, and peek() / step() / seek() / jump() keep returning the starting** values (never the target). Calling during(0U) is the same. Always call during() with a positive duration for each segment you intend to animate.

auto tween = tweeny::from(0).to(100).during(60U).build();

For multi-value tweens, you can specify either:

  • A single duration that applies to all values
  • Individual durations for each value (must match the number of values)
// Same duration for all values (60 frames)
auto t1 = tweeny::from(0, 0, 0).to(100, 200, 300).during(60U).build();
// Different durations per value
auto t2 = tweeny::from(0, 0, 0).to(100, 200, 300).during(30U, 60U, 90U).build();

When using per-value durations, the total interpolation length is determined by the longest duration. In the example above, the first value reaches its target at frame 30, the second at frame 60, and the third at frame 90. The interpolation is complete when all values have reached their targets (at frame 90).

Easing Functions

Easing functions control how values interpolate between keyframes. They take a progress value (0.0 to 1.0), a start value, and an end value, then return the interpolated value at that progress. For example, a linear easing is simply:

int linear(float p, int a, int b) {
return static_cast<int>((b - a) * p + a);
}

By default, tweens use easing::def (an alias of easing::linear). You can change this with the via() method, which must be called after to(). Tweeny includes 30+ built-in easing functions:

auto tween = tweeny::from(0).to(100).during(60U).via(tweeny::easing::quadraticInOut).build();
constexpr detail::quadraticInOutEasing quadraticInOut
Quadratic polynomial easing with gentle acceleration and deceleration.
Definition easing.h:1069

Like during(), you can specify easings per-value or use the same for all values:

// Same easing for all values
auto t1 = tweeny::from(0, 0, 0).to(100, 200, 300).during(60U)
.build();
// Different easing per value
auto t2 = tweeny::from(0, 0, 0).to(100, 200, 300).during(60U)
.via(
)
.build();
constexpr detail::linearEasing linear
Linear easing function with constant velocity throughout the animation.
Definition easing.h:956
constexpr detail::bounceOutEasing bounceOut
Bounce easing simulating a ball dropping and bouncing to rest.
Definition easing.h:259
constexpr detail::quadraticOutEasing quadraticOut
Quadratic polynomial easing (t²) with gentle deceleration.
Definition easing.h:1033
Contains all built-in easing functions for controlling animation curves.
Definition by-name.h:32

See tweeny::easing namespace documentation for all available easings, or visit http://easings.net for visualizations.

Runtime Easing Selection

When the easing must be chosen at runtime (for example from configuration or user input), use easing::byName(). It returns a callable usable with via():

auto tween = tweeny::from(0).to(100).during(60U)
.via(tweeny::easing::byName("cubicInOut"))
.build();
A tween represents an animation between keyframes.
Definition tween.h:68
byNameEasing byName(std::string_view name)
Select a bundled easing by its identifier name.
Definition by-name.h:178

Names match the identifier names exactly (linear, cubicInOut, bounceOut, …). Unknown names throw std::invalid_argument at the call to byName().

Custom Easing Functions

You can provide custom easing functions as any callable matching the signature T(float, T, T):

auto tween = tweeny::from(0).to(100).during(60U)
.via([](float p, int a, int b) {
return static_cast<int>((b - a) * p * p + a); // Quadratic
})
.build();

For heterogeneous tweens, each easing must match its corresponding value type:

auto tween = tweeny::from(0, 1.0f).to(100, 200.0f).during(60U)
.via([](float p, int a, int b) { return (b - a) * p + a; },
[](float p, float a, float b) { return (b - a) * p + a; })
.build();
Note
Most easing functions truncate (not round) when returning integral types. This can cause interpolations to appear "stuck" at the start value for a while. Use floating-point types and round manually for smoother results, or use easing::linear which handles this correctly for integers.

Multi-Point Animations

You can create complex interpolations by chaining multiple keyframes together. Each call to to() adds a new keyframe, and subsequent calls to during() and via() configure that specific segment:

.to(100).during(500U) // 0 → 100 (linear, 500 frames)
.to(200).during(100U).via(easing::bounceOut) // 100 → 200 (bounce, 100 frames)
.to(50).during(200U).via(easing::backInOut) // 200 → 50 (back, 200 frames)
.build();
constexpr detail::backInOutEasing backInOut
Easing function combining anticipation at the start and overshoot at the end.
Definition easing.h:171

The resulting tween seamlessly transitions through all keyframes. Navigation methods like step() and seek() work transparently across keyframe boundaries.

Navigating Tweens

Once built, a tween can be navigated in three ways: stepping, seeking, and jumping.

Stepping

Stepping moves the tween by a relative amount (delta). This is the primary method for frame-by-frame interpolation in game loops:

auto tween = tweeny::from(0).to(100).during(1000U).build();
while (tween.progress() < 1.0f) {
int value = tween.step(1); // Advance by 1 frame
// Use value...
}
auto step(int32_t frames) -> tween_value_t
Advances or rewinds the animation by a frame delta.
auto progress() const -> float
Returns the animation completion percentage.

step() accepts a signed 32-bit integer (int32_t). Positive values move forward, negative values move backward:

tween.step(10); // Move forward 10 frames
tween.step(-5); // Move backward 5 frames

Seeking

Seeking jumps to an absolute frame position. Useful for scrubbing or jumping to specific points:

auto tween = tweeny::from(0).to(100).during(1000U).build();
tween.seek(500U); // Jump to frame 500 (50% complete)
tween.seek(0U); // Jump back to start
auto seek(uint32_t target_frame) -> tween_value_t
Seeks to a specific frame in the animation.

seek() accepts an unsigned 32-bit integer (uint32_t) representing the absolute frame number. Values are clamped to the valid frame range, from the first keyframe's position to the last keyframe's position.

Jumping to Keyframes

Jumping moves directly to a keyframe by its index (0-based). This is useful for multi-point interpolations:

auto tween = tweeny::from(0).to(100).during(100U).to(200).during(100U).build();
tween.jump(0); // Jump to keyframe 0 (value: 0, frame: 0)
tween.jump(1); // Jump to keyframe 1 (value: 100, frame: 100)
tween.jump(2); // Jump to keyframe 2 (value: 200, frame: 200)
auto jump(std::size_t target_key_frame) -> tween_value_t
Jumps to a specific keyframe index.

Return Values

All navigation methods (step(), seek(), jump()) return the current interpolated value(s):

Peeking Values

Use peek() to query the current value without modifying the tween's state:

auto tween = tweeny::from(0).to(100).during(100U).build();
tween.step(50);
int current = tween.peek(); // Returns 50, doesn't change state
int preview = tween.peek(75U); // Preview value at frame 75, doesn't move tween
auto peek() const -> tween_value_t
Returns the current interpolated value without changing state.

Progress

Use progress() to query how far the tween has advanced as a normalized float in [0, 1]. Like peek(), it does not mutate the tween: peek() answers “what value?”, progress() answers “how far in time?”.

auto tween = tweeny::from(0).to(100).during(100U).build();
tween.step(50);
float p = tween.progress(); // 0.5f

Event System

Tweeny provides an event system for reacting to interpolation lifecycle events. Register callbacks using the tween::on() method with an event type tag.

Event Types

Available event types:

When several apply to the same call, they fire in this order: specific (step|seek|jump)updatecomplete (when complete applies).

Basic Callbacks

Most callbacks receive a reference to the tween and return an event::response:

auto tween = tweeny::from(0, 0).to(100, 200).during(100U).build();
auto [x, y] = t.peek();
printf("Position: (%d, %d), Progress: %.2f\n", x, y, t.progress());
});
// Fires after step/seek/jump, before complete when applicable
printf("Tween updated to: %d\n", t.peek());
});
// Completion callback
printf("Animation finished!\n");
});
auto on(detail::event::step_t, Callback &&cb) -> void
Registers a callback for step() events.
constexpr detail::event::complete_t complete
Event triggered when the animation is at completion.
Definition event.h:174
@ ok
Continue receiving events.
Definition detail/event.h:108
constexpr detail::event::update_t update
Event triggered whenever the tween position changes.
Definition event.h:280
constexpr detail::event::step_t step
Event triggered after each step() call.
Definition event.h:83

Keyframe Callbacks

Keyframe events receive additional data through an event struct:

auto tween = tweeny::from(0).to(50).during(50U).to(100).during(50U).build();
tween.on(tweeny::event::keyframeEnter, [](auto& t, auto evt) {
printf("Entering keyframe %zu\n", evt.key_frame);
});
tween.on(tweeny::event::keyframeLeave, [](auto& t, auto evt) {
printf("Leaving keyframe %zu\n", evt.key_frame);
});
Event data passed when entering a new keyframe.
Definition detail/event.h:81
Event data passed when leaving a keyframe.
Definition detail/event.h:92

Callback Lifetime

The return value controls whether a callback stays registered:

int step_count = 0;
tween.on(tweeny::event::step, [&](auto& t) {
step_count++;
if (step_count >= 10) {
printf("Unsubscribing after 10 steps\n");
}
});
@ unsubscribe
Unsubscribe after this callback.
Definition detail/event.h:116

Callable Types

Any callable matching the required signature can be used - lambdas, function pointers, functors, etc:

// Lambda (most common)
});
// Function
auto my_callback = [](tweeny::tween<int>& t) {
};
constexpr detail::event::seek_t seek
Event triggered after each seek() call.
Definition event.h:111

Done!

Enjoy using Tweeny!