Tweeny: header-only tweening with a fluent builder API in C++17
A modern C++ tweening library
At a glance
- What is it?
- Tweeny is a header-only C++17 library for interpolating values over time, built around a builder that you finish with build() before you get a tween. It supports 30 plus easing functions, multi-point keyframes and an event system, and version 4 changed the API in ways that require a migration.
- Who is it for?
- Tweeny suits a C++17 game or interactive project that needs to animate arbitrary arithmetic values without pulling in a heavier animation framework, and the zero dependency, header-only design means adding it costs a copy of a folder. It is the wrong choice for a codebase still on the 3.x API, since 4.x introduces breaking changes that need the documented migration path.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 21 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The builder API means from() does not give you a tween yet
The central design decision in Tweeny 4.x is that tweeny::from() returns a builder, not a tween. You declare the interpolation, and then you call build() to get the object you step. That is a small thing, but it is the reason the library has a fluent API at all: from, to, via and during are chained on a builder that is only meaningful once it is complete.
The canonical example in the project description animates a string one character at a time, which is a better demonstration of the type system than the usual float example. Each character is a separate interpolable value, the tuple comes back from a single step call, and structured bindings unpack it.
#include <tweeny/tweeny.h>
auto helloworld = tweeny::from('h', 'e', 'l', 'l', 'o')
.to('w', 'o', 'r', 'l', 'd')
.during(50U)
.build();
for (int i = 0; i < 50; i++) {
auto [w, o, r, l, d] = helloworld.step(1);
printf("%c%c%c%c%c\n", w, o, r, l, d);
}The claim behind this is type safety at compile time. Because the interpolated type comes from the arguments to from(), a tween that should carry a float cannot silently be handed an int, and heterogeneous sets are supported, which is how you animate a position and a colour and an opacity in one tween and read them back as a tuple.
The stated goal is narrow and practical: set the starting values, step the tween each frame, and plug the result back into your object. There is no scene graph, no timeline asset format, no runtime object model. The library interpolates.
Easing is a separate link in the chain, and keyframes stack through it
Easing functions are not a property of the tween but of a segment. The via() call attaches an easing to the segment being declared, so a multi-point animation gives each leg its own curve.
using tweeny::easing;
// Easing
auto smooth = tweeny::from(0.0f)
.to(100.0f)
.via(easing::quadraticInOut)
.during(60U)
.build();
// Multi-segment keyframe animation
auto path = tweeny::from(0)
.to(50).via(easing::linear).during(30U)
.to(100).via(easing::bounceOut).during(30U)
.build();The library ships more than thirty easing functions and also accepts custom ones, so the curve is not limited to the built-in set. The multi-segment form above is the keyframe mechanism: two chained to() calls with different easings and different durations produce a single tween that runs the first leg then the second.
That arrangement is worth noting because it is where most tweening libraries differ. A library that exposes one easing per tween forces you to build several tweens and schedule them yourself. Here the segment carries the easing, so one tween covers the whole path, and reading a value gives you the value at the current point in that path.
The easing reference for what the curves look like is easings.net, which the project links as the visualisation source.
Events fire on step, seek, jump, update, completion and keyframe boundaries
The event system is the third pillar next to easing and keyframes, and it covers six situations: step, seek, jump, update, completion, and entering or leaving a keyframe. Handlers register with on() and receive the tween, and each returns a response value.
auto tween = tweeny::from(0).to(100).during(60U).build();
tween.on(tweeny::event::update, [](auto& t) {
printf("now at %d\n", t.peek());
return tweeny::event::response::ok;
});
tween.on(tweeny::event::complete, [](auto& t) {
printf("done at %d\n", t.peek());
return tweeny::event::response::ok;
});The distinction between step and update matters in practice. Step is the discrete advance of the tween, which is what you trigger each frame. Update is the notification that the value changed, and the peek() call in the example reads the current value without disturbing it, which is what you want when you are about to copy it into your object's fields.
Control is timeline-like rather than forward-only. The project lists seek, jump and backward stepping alongside the forward step, so a tween is addressable: you can move to a specific point, jump to the end, and step backwards with a negative value. The migration notes for 4.x make the same point about types: durations are uint32_t written with a U suffix such as 60U, and steps are int32_t where negative values step backward.
For a game loop, that means a tween can be inspected and repositioned rather than only played forward, which is what you need when a state machine interrupts an animation and later wants to resume it.
Header-only means the install is a copy, a single header or a CMake target
Tweeny has zero external dependencies and is header-only, so there is no library to link against and no build artefact to ship. That makes the install decision a packaging decision rather than a compilation one.
The simplest route is to copy the include/ directory into your project and include the single header.
#include <tweeny/tweeny.h>The second route is the single-header release file. Each release glues the code into one file named after the version, so you drop tweeny-4.0.0.h into your tree and include it by that name.
#include "tweeny-4.0.0.h"The third is a CMake subproject, which registers the library as a target in your own build.
add_subdirectory(tweeny)
target_link_libraries(yourtarget PRIVATE tweeny::tweeny)That form adds the include directory to your target and requires C++17. The fourth route installs the headers and the CMake package config so another project can find it.
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake --install build --prefix /path/to/prefixfind_package(Tweeny 4 CONFIG REQUIRED)
target_link_libraries(yourtarget PRIVATE tweeny::tweeny)Note that find_package takes a version argument of 4, so the package config is what enforces the major version. If CMake cannot find the prefix, point it at one with CMAKE_PREFIX_PATH, as the project documentation instructs.
There is also a package manager route. Repology tracks the distribution packages that third parties have built, with versions listed on its project page, and the project credits the people who maintain them.
Migrating from 3.x touches four separate parts of your call sites
Tweeny 4.x introduces breaking API changes, and the project keeps the previous README in README-3.md so you can compare. The migration summary lists four differences, and each one has a mechanical fix.
The first is that from() returns a builder, so every existing call site needs a build() appended. The second is a type change: durations move to uint32_t with the U suffix, and steps become int32_t where negative values step backward. If your code passed an int literal for a duration, that call no longer compiles in the way it used to, and the fix is a literal change rather than a cast.
The third is the callback API. The old onStep() and onSeek() methods are replaced by a single on(event::step, ...) and on(event::seek, ...) form, which folds several callbacks into one registration mechanism. The fourth is that multi-value tweens return a std::tuple, so the results are read with structured bindings rather than by indexed access.
The full migration guide is not in the README. It lives in the Doxygen documentation, which you build with the documentation flag enabled and the doc target.
cmake -B build -DTWEENY_BUILD_DOCUMENTATION=ON
cmake --build build --target docThat is the sensible first action for anyone evaluating the upgrade, because the diff between the two APIs is bigger than four bullet points once you start counting call sites in a real project.
Where Tweeny is the wrong tool
The first wrong-tool case is a project that needs more than interpolation. Tweeny animates values. It does not manage a scene, it does not own your object graph, and it does not schedule tweens against a timeline asset. If your requirement is an authoring tool with a visual editor, this library is not that, and no amount of configuration will turn it into one.
The second case is a codebase that cannot move to C++17. The library requires C++17, and that is stated as a hard requirement of the CMake integration, not a suggestion. A legacy engine pinned to C++11 has to stay on the 3.x line, which also means it stays on the previous API and misses the event system rework and the keyframe enter and leave events.
The third case is a project that already depends on a full animation framework. Adding a second tween system means two conventions for easing and two ways to represent an in-flight animation, and the coordination cost usually exceeds the benefit of a small header-only library.
On maintenance and licensing, the project is MIT licensed with the LICENSE file in the repository root, which is a short permissive grant with no source disclosure obligation. The version history shows active work rather than an abandoned project: v4.0.0 and v4.1.0 both landed on 2026-08-03, and the last push was on 2026-10-02, so the header you copy is likely to keep changing across patch releases. If you vendor the include directory rather than depending on a package, you take on the job of tracking those changes yourself.
Editorial conclusion
Tweeny suits a C++17 game or interactive project that needs to animate arbitrary arithmetic values without pulling in a heavier animation framework, and the zero dependency, header-only design means adding it costs a copy of a folder. It is the wrong choice for a codebase still on the 3.x API, since 4.x introduces breaking changes that need the documented migration path. Before adopting, read README-3.md against your current calls, then build the Doxygen documentation with -DTWEENY_BUILD_DOCUMENTATION=ON for the full migration guide, and check the Repology package list if you would rather install a packaged build than copy the include directory.
Frequently asked questions
How do I install Tweeny in a C++ project?
Tweeny is header-only with no external dependencies, so you can copy the include/ directory and include tweeny/tweeny.h, drop in the release single-header file named after the version, use add_subdirectory with the tweeny::tweeny target, or install the package config and consume it with find_package(Tweeny 4 CONFIG REQUIRED).
What changed between Tweeny 3.x and 4.x?
from() now returns a builder that needs build(), durations use uint32_t such as 60U, steps use int32_t with negative values stepping backward, callbacks use on(event::step, ...) instead of onStep() and onSeek(), and multi-value tweens return a std::tuple for structured bindings. The previous README is kept in README-3.md and the full migration guide is in the Doxygen documentation.
How many easing functions does Tweeny provide?
The project lists more than thirty easing functions, and the via() call attaches one to a segment of the tween rather than to the whole tween, so different legs of a multi-point keyframe animation can use different curves. Custom easings are also supported.
Which events does Tweeny support?
The documented event types are step, seek, jump, update, completion, and keyframe enter and leave. Handlers are registered with on() and return a response value such as tweeny::event::response::ok.
What license is Tweeny released under?
Tweeny is licensed under the MIT License, with the LICENSE file at the root of the repository. It requires C++17 and, being header-only with no dependencies, adds no linking obligations.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mobius3-tweeny)Was this useful?