A TypeScript-first tweening engine forked from the excellent @tweenjs/tweenjs.
Popular UI frameworks supported:
Your favorite framework isn't listed? Let us know!
State-first architecture with validation before runtime, not during. Explicit configuration, zero guesswork, minimal overhead.
- SSR-compatible hooks for React, SolidJS, Svelte, Preact, and Vue β provide initial values for server rendering, skip initialization entirely on the server
- Predictable outcomes through upfront value validation β invalid values prevent animation start with clear feedback
- Production-ready validation system catches configuration errors before they break your app
- Natural reverse playback via inverted easing (no
reverseEasingoption orvaluesStartreassignment) - Extensible interpolation with built-in extensions for custom per-property validators and interpolators
- Single shared
requestAnimationFrameloop for all tweens/timelines with automatic start/stop - Zero GC pressure via specialized
miniStorefor framework integrations - Tuple-based hot update runtime eliminates object lookup
- No validation during updatesβall checks happen at initialization
whileloops throughout for maximum speed
- Tween Guide - the official
Tweendocumentation - Timeline Guide - the official
Timelinedocumentation - Easing Guide - the easing functions documentation
- Extend Guide - the extensions documentation
- Troubleshooting - a quick check on issues and how to solve them.
- Ministore - an inside look at
miniStore.
- The original Tween.js User Guide can also provide valuable tips.
npm install @thednp/tween
pnpm add @thednp/tween
deno add @thednp/tween
bun add @thednp/tween
<script src="https://cdn.jsdelivr.net/npm/@thednp/tween/dist/tween.min.js"></script>
<script>
const { Tween, Easing } = TWEEN;
const tween = new Tween({ x: 0 });
</script>To use Tween and Timeline with UI frameworks please check the dedicated sections: React, SolidJS, Svelte, Preact and Vue.
import { Tween, Easing } from '@thednp/tween';
// find some target
const target = document.getElementById('my-target');
// define a tween
const tween = new Tween({ x: 0 })
.duration(1.5) // duration/delay accept seconds (e.g., 1.5 = 1.5s)
.onUpdate((obj, elapsed) => {
// manipulate the DOM directly
Object.assign(target.style, { translate: obj.x + "px" });
// monitor progress of the tween
console.log(`Tween progress: ${Math.floor(elapsed * 100)}%`)
});
// override any value on the fly
const moveRight = () => tween
.from({ x: 0 }) // override/reset start values
.to({ x: 150 }) // override end values
.easing(Easing.Quadratic.Out) // set a special easing function for every case
.duration(1.5) // set duration as well in seconds
.start(); // start the tween
const moveLeft = () => tween
.from({ x: 150 }) // set a different from
.to({ x: -150 }) // set a different to
.easing(Easing.Elastic.Out) // override easing
.duration(1.5) // override duration in seconds
.start(); // start the tween
// trigger any time
const button1 = document.getElementById('my-button-1');
const button2 = document.getElementById('my-button-2');
button1.onclick = moveRight;
button2.onclick = moveLeft;
// The engine does requestAnimationFrame/cancelAnimationFrame for youFor an extended guide, check the Tween Wiki.
import { Timeline, Easing } from '@thednp/tween';
// find some target
const target = document.getElementById('my-target');
// define a timeline
const myTimeline = new Timeline({ x: 0, y: 0 })
.to({ x: 150, duration: 2.5, easing: Easing.Elastic.Out })
.to({ y: 150, duration: 1.5, easing: Easing.Elastic.Out }, "-=1")
.onUpdate((obj, elapsed) => {
// manipulate the DOM directly
Object.assign(target.style, {
translate: obj.x + "px " + obj.y + "px",
});
// monitor progress of the timeline
console.log(`Timeline progress: ${Math.floor(elapsed * 100)}%`)
});
// trigger any time
const button = document.getElementById('my-button');
button.onclick = myTimeline.play();
// The engine does requestAnimationFrame/cancelAnimationFrame for youFor an extended guide, check the Timeline Wiki.
Simple tween objects with essential controls, callbacks, and sequencing methods.
Complex scheduling with per-property duration, delay, and easing. Includes seek() and label() for precise control.
Built-in and custom per-property validators and interpolators. Single-level plain objects only.
All values validated on initialization from initialValues (source of truth). Invalid configurations prevent execution with actionable feedback.
Shared requestAnimationFrame loop starts on first start() / play(), stops when queue empties.
Not Implemented
chain()featureonEveryStart,onFirstStartcallbacks- The original Tween.js array interpolation
- Deeply nested objects
- The original Tween.js dynamic end values
Changes
duration(),delay(),repeatDelay(),seek()accept values in seconds (converted to milliseconds internally)- Automatic RAF queue system (you don't need to define a global
requestAnimationFrameupdate loop yourself) - Reverse playback via inverted easing (no
reverseEasingoption required) - Per-property extensions via
.use('propName', extensionConfig)
Single requestAnimationFrame loop managed by Runtime.ts:
tween.start()/timeline.play()adds instance to global queueRuntime()calls.update(time)on all queued items each frame- Instances returning false (finished/stopped) are removed
- Empty queue triggers automatic
cancelAnimationFrame
Updates are async by design:
start()/play()queues instance- Next RAF tick β
Runtime()βupdate(time)β interpolation - DOM/state updates on subsequent frames
- Visual changes sync with display refresh rate for smooth animations.
- No DOM access in core
- RAF calls browser-only (via
Runtime()) now()defaults toperformance.now()(can fallback to Date.now() for Node)- Framework hooks include SSR guards and provide values for server-rendered HTML
- Important: Don't call
start()/play()during SSR.
- Chaining β use
onCompletecallback to trigger next tween/timeline - Custom interpolation β register per-property extensions with
.use()
For any issue or unclear guides, please file an issue and help make this guide better. Or feel free to submit a PR! Thank you!
How to contribute:
- fork the project
- change code/docs & update tests
- submit PR