tween-functions
A tiny JavaScript library of Robert Penner's easing functions for smooth animation interpolation.
Repository Health
Technical Analysis
tween-functions packages Robert Penner’s classic easing equations into a single, dependency-free JavaScript module. Each function takes the current time, start value, end value, and total duration, then returns the interpolated value for that instant — the same signature used throughout animation libraries, so it drops into any tweening or rendering loop without pulling in a larger animation framework.
Originally extracted to power React-tween-state and React-state-stream, the library ships all 30 standard Penner easing curves — linear through quint, sine, expo, circ, elastic, back, and bounce, each in in/out/in-out variants — as plain functions with no internal state. It’s the kind of narrow, well-tested math utility you install once and forget, useful anywhere a time value needs to become a smoothly-curved progress value: custom animation loops, drag/scroll physics, or chart transitions.
What You Get
- All 30 Penner easing functions (linear, quad, cubic, quart, quint, sine, expo, circ, elastic, back, bounce) each with in/out/in-out variants
- A single, dependency-free module with one consistent function signature: (currentTime, beginValue, endValue, totalDuration)
- Math extracted from and proven in chenglou’s react-tween-state and react-state-stream libraries
- Zero configuration — require the module and call the named easing function directly, no setup or instantiation needed
Common Use Cases
- Driving animation interpolation inside a custom render or requestAnimationFrame loop
- Powering React animation libraries such as react-tween-state that need raw easing math without a full animation engine
- Adding natural-feeling motion to drag, scroll, or gesture-driven UI transitions
- Generating eased value sequences for canvas, WebGL, or game-loop animations
Under The Hood
Architecture
tween-functions has essentially no architecture beyond a single flat object literal: index.js defines one tweenFunctions object containing all 30 easing methods and exports it directly via module.exports. There’s no class hierarchy, no dependency injection, and no internal state — every function is a pure computation over its four (or five, for the Back variants) numeric arguments. The one structural wrinkle is that the composite bounce functions (easeInBounce, easeInOutBounce) call back into the exported tweenFunctions object itself rather than local function references, so the module’s internal cross-calls are bound to the object at call time rather than to local closures — a subtle coupling that would surface if the file were ever split into separate per-function modules.
Tech Stack
The library is written in plain ES5-style JavaScript ('use strict', var, CommonJS require/module.exports) with zero runtime dependencies declared in package.json. There’s no build step, bundler, or transpiler — index.js is shipped and consumed as-is. The package.json test script is a stub that echoes an error and exits non-zero, and there’s no CI configuration in the repository, meaning nothing currently validates changes automatically.
Code Quality
There are no test files anywhere in the repository, and the npm test script explicitly does nothing but print an error and exit with a failure code — testing was never wired up. The 6KB index.js is internally consistent in naming (Penner’s canonical easeIn/Out/InOut<Curve> convention) and argument order, but there is no type safety (no TypeScript, no JSDoc annotations), no linter or formatter configuration, and no CI pipeline. Error handling is absent by design — the functions assume well-formed numeric input.
API Design
The public API is a single object with 30 methods sharing one consistent signature — (currentTime, beginValue, endValue, totalDuration) — which mirrors Robert Penner’s original easing-equation convention closely enough that anyone familiar with Penner’s canonical implementations can use this module with zero learning curve. There’s no configuration, no class to instantiate, and no async boilerplate: consumers require() the module and call the named function directly. The tradeoff is limited discoverability beyond the README’s function list — no TypeScript definitions, no in-code JSDoc, and no grouping of the 30 exported curves beyond their order in the source file.