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