Mirror of a package developed in a private monorepo — issues and pull requests are welcome here.
Modern TypeScript implementations of the classic Penner easing functions with physics-based parameters and tree-shakeable exports.
- Tree-shakeable: Import only the easing functions you need
- Physics-based: Configure easings with intuitive physical parameters
- TypeScript: Full type safety with comprehensive interfaces
- Modern API: Clean factory functions with sensible defaults
- High performance: Optimized implementations with numerical stability
npm install @penner/easingimport{back,bounce,physicsSpring}from"@penner/easing";// Each family is callable with a config and returns { in, out, inOut, outIn }constbounceOut=bounce({bounces: 4,decay: 0.95}).out;constbackOut=back({overshoot: 0.15}).out;constspringOut=physicsSpring({bounces: 4,decay: 0.9}).out;// Defaults are also available as properties — no call neededconstdefaultBackOut=back.out;// Use in animationsconstprogress=bounceOut(0.5);// Returns the eased value at t=0.5Custom polynomial easings with any exponent, including fractional powers.
import{power}from"@penner/easing";// Create custom power familiesconstsqrt=power({power: 0.5});// Square root easingconstcustom=power({power: 2.5});// t^2.5 easingconstsqrtOut=sqrt.out;constcustomIn=custom.in;// All variantsconstfam=power({power: 1.7});consteaseIn=fam.in;consteaseOut=fam.out;consteaseInOut=fam.inOut;consteaseOutIn=fam.outIn;Unified family that combines Power's exponent with Back's anticipation. For exponent > 1, the curve dips below 0 (anticipation) before accelerating to 1 — generalizing classic cubic Back to any power.
import{flex}from"@penner/easing";// Defaults (power: 3, overshoot: 0.1) — equivalent to classic BackconsteaseIn=flex.in;consteaseOut=flex.out;// Custom: stronger anticipation with a steeper accelerationconstsnappy=flex({power: 4,overshoot: 0.2});constsnappyOut=snappy.out;constsnappyInOut=snappy.inOut;interfaceFlexConfig{power: number;// Power exponent (> 1 for overshoot; default 3)overshoot: number;// Anticipation depth as a fraction (default 0.1)}Setting overshoot: 0 collapses to pure power easing (u^n); power: 3 with a custom overshoot matches classic back.
Creates overshoot effects where the animation goes beyond its target before settling.
import{back}from"@penner/easing";// Default overshoot (10%) — use the family's default variants directlyconsteaseOut=back.out;// Custom overshoot (20%)consteaseOutBig=back({overshoot: 0.2}).out;// All variantsconsteaseIn=back({overshoot: 0.15}).in;consteaseInOut=back({overshoot: 0.1}).inOut;consteaseOutIn=back({overshoot: 0.1}).outIn;Physics-based bouncing with configurable energy loss and number of bounces.
import{bounce}from"@penner/easing";// Default bounce (4 bounces, 95% decay)consteaseOut=bounce.out;// Custom bounceconsteaseOutBouncy=bounce({bounces: 6,decay: 0.8,}).out;// All variantsconsteaseIn=bounce({bounces: 3,decay: 0.9}).in;consteaseInOut=bounce.inOut;consteaseOutIn=bounce.outIn;Bounce easing that overshoots above the target before settling through decaying parabolic bounces.
import{overBounce}from"@penner/easing";// Default: 0.3 overshoot, 3 bounces, 60% decayconsteaseOut=overBounce.out;// Higher overshootconsteaseOutBig=overBounce({overshoot: 0.5,bounces: 4}).out;// Specify initial velocity instead of overshootconsteaseOutFast=overBounce({v0: 4.0,bounces: 2}).out;// All variantsconsteaseIn=overBounce.in;consteaseInOut=overBounce.inOut;consteaseOutIn=overBounce.outIn;Damped oscillations with configurable bounces and decay.
import{physicsSpring}from"@penner/easing";// Default spring (4 bounces, 95% decay)consteaseOut=physicsSpring.out;// Custom oscillationsconsteaseOutCustom=physicsSpring({bounces: 6,decay: 0.9,}).out;// Critically damped (no oscillation)consteaseOutSmooth=physicsSpring({bounces: 0}).out;// All variantsconsteaseIn=physicsSpring.in;consteaseInOut=physicsSpring.inOut;consteaseOutIn=physicsSpring.outIn;Overdamped spring easing for smooth, non-oscillating motion. Single-knob API with a normalized [0, 1] overdamping parameter.
import{overdampedSpring}from"@penner/easing";// Default overdamped spring (overdamping = 0.5, ζ = 2)consteaseOut=overdampedSpring.out;// Sharper arrival (closer to critical damping)constsharp=overdampedSpring({overdamping: 0.2}).out;// Languid, near-linear approachconstheavy=overdampedSpring({overdamping: 0.85}).out;// Endpoint delegations:constcritical=overdampedSpring({overdamping: 0}).out;// critically dampedconstlinear=overdampedSpring({overdamping: 1}).out;// linear easing// All variantsconsteaseIn=overdampedSpring.in;consteaseInOut=overdampedSpring.inOut;consteaseOutIn=overdampedSpring.outIn;overdamping maps to the damping ratio ζ via soft saturation: ζ = 1 / (1 − overdamping). For exact damping-ratio control or cross-tool spring-physics compatibility, use physicsSpring({ bounces: 0, dampingRatio }) (planned).
Spring easing that oscillates below the target but never exceeds 1. Useful for animations where overshoot is undesirable (e.g., opacity, corner radius).
import{innerSpring}from"@penner/easing";// Default (1 bounce, 95% decay)consteaseOut=innerSpring.out;// More oscillationsconsteaseOutBouncy=innerSpring({bounces: 3}).out;// Custom decayconsteaseOutCustom=innerSpring({bounces: 2,decay: 0.5}).out;// All variantsconsteaseIn=innerSpring.in;consteaseInOut=innerSpring.inOut;consteaseOutIn=innerSpring.outIn;Spring easing that overshoots above the target and oscillates back down to settle at 1. After the initial cruise phase, the curve always stays at or above 1.
import{outerSpring}from"@penner/easing";// Default (1 bounce, 95% decay)consteaseOut=outerSpring.out;// More oscillationsconsteaseOutBouncy=outerSpring({bounces: 3}).out;// Less aggressive decayconsteaseOutCustom=outerSpring({bounces: 2,decay: 0.5}).out;// All variantsconsteaseIn=outerSpring.in;consteaseInOut=outerSpring.inOut;consteaseOutIn=outerSpring.outIn;Classic polynomial and trigonometric easing functions. Each is a StandardEasingFamily with .in, .out, .inOut, and .outIn properties.
import{quadratic,cubic,quartic,quintic,sine,circular,exponential}from"@penner/easing";// Access variants as properties (not function calls)constquadOut=quadratic.out;constcubicIn=cubic.in;constquartInOut=quartic.inOut;constsineOutIn=sine.outIn;Generalizes circular easing via the Lamé curve 1 = linear, 2 = circular, →∞ = square (step-like).
import{superellipse}from"@penner/easing";// Circular (identical to the `circular` family)constcirc=superellipse({power: 2});// Softer than circularconstsoft=superellipse({power: 1.5});// Sharper, more squared-offconstsharp=superellipse({power: 4});// All variantsconsteaseIn=sharp.in;consteaseOut=sharp.out;consteaseInOut=sharp.inOut;consteaseOutIn=sharp.outIn;interfaceSuperellipseConfig{/** Superellipse exponent (1 = linear, 2 = circular, →∞ = square) */power: number;}Simple easing functions exported as single EasingFn values (not families).
import{linear,smoothstep,smootherstep,smootheststep}from"@penner/easing";constvalue=smoothstep(0.5);// C¹ Hermite (degree 3)constvalue2=smootherstep(0.5);// C² (degree 5)constvalue3=smootheststep(0.5);// C³ (degree 7)smoothstepN(n) is the generalized factory — returns the degree-(2n+1) polynomial with n vanishing derivatives at both endpoints:
import{smoothstepN}from"@penner/easing";// smoothstepN(1) ≡ smoothstep, smoothstepN(2) ≡ smootherstep, smoothstepN(3) ≡ smootheststepconstcustom=smoothstepN(4);// degree 9, C⁴ smoothConfigurable exponential easing with utility functions.
import{exponential,makeExponentialEaseOut}from"@penner/easing";// Standard exponential familyconsteaseOut=exponential.out;consteaseIn=exponential.in;// Custom exponential ease-outconstcustomExpo=makeExponentialEaseOut(10);Physics-based easing functions derived from force models. These are experimental and their APIs may change.
import{force,compression,viscous,viscousPower,swim}from"@penner/easing";Includes force, compression, viscous, viscousDrag, viscousPower, swim, and swimAnalytic.
Import only what you need to keep bundle sizes small:
// Import specific familiesimport{bounce,physicsSpring}from"@penner/easing";// Import standard easings alongside physics-based onesimport{quadratic,cubic,back}from"@penner/easing";interfaceBounceConfig{bounces?: number;// Number of bounces (default: 4)decay?: number;// Total height decay as fraction 0-1 (default: 0.95)}interfaceSpringPhysicsConfig{bounces?: number;// Visible oscillation half-cycles (default: 4)decay?: number;// Total amplitude decay as fraction 0-1 (default: 0.95)}easingKit wraps any easing function into a callable bundle with CSS linear() approximations, velocity curve, and derivative functions — all lazily computed and cached on first access.
import{easingKit,physicsSpring}from"@penner/easing";// Destructure the CSS strings you needconst{ easing, velocity }=easingKit({easingFn: physicsSpring.out()});// Use easing for position, velocity for scaleelement.style.animation="move 2s both, scale 2s both";element.style.animationTimingFunction=`${easing}, ${velocity}`;@keyframes move {
to {
translate:200px;
}
}
@keyframes scale {
from {
scale:0.8;
}
}// Or with the Web Animations APIelement.animate([{translate: "0px"},{translate: "200px"}],{duration: 2000,
easing,fill: "both",});element.animate([{scale: 0.8},{scale: 1}],{duration: 2000,easing: velocity,fill: "both"});// Or keep the kit for callable use and derivativesconstkit=easingKit({easingFn: spring.out()});kit(0.5);// call as a plain easing functionkit.velocityFn(0.5);// numerical 1st derivativekit.accelerationFn(0.5);// numerical 2nd derivativeYou can supply exact analytical derivatives when the particular math formulas are known:
constkit=easingKit({easingFn: (t)=>t*t,velocityFn: (t)=>2*t,// exact derivative of t²});interfaceEasingKitOptions{easingFn: EasingFn;velocityFn?: VelocityFn;// analytical 1st derivativeaccelerationFn?: AccelerationFn;// analytical 2nd derivativejerkFn?: JerkFn;// analytical 3rd derivativemeta?: EasingKitMeta;// factory name + args for serialization}typeEasingKit=EasingFn&{readonlyeasingFn: EasingKit;// self-reference for destructuringreadonlyeasing: CSSEasing;// CSS linear() stringreadonlyvelocityFn: VelocityFn;readonlyaccelerationFn: AccelerationFn;readonlyjerkFn: JerkFn;readonlyvelocity: CSSEasing;// velocity as CSS linear()readonlymeta: EasingKitMeta;readonlytoString: ()=>CSSEasing;};Standalone utilities for working with easing functions. For bundled derivatives and CSS, see EasingKit above.
import{reverseEasingFn,mirrorEasingFnToRight,mirrorEasingFnToLeft,easingFnToCssLinear,createVelocityFn,createAccelerationFn,createJerkFn,withV0,withV0Family,softsignClamp,pruneColinearPoints,progressWave,}from"@penner/easing";// Reverse an easing function (swap start and end)constmyEaseIn=reverseEasingFn(quadratic.out);// Mirror an easing to the right (ease-in becomes ease-in-out)constmyEaseInOut=mirrorEasingFnToRight(quadratic.in);// Clamp values to 0-1 rangeconstsafe=clamp01(someValue);// Convert an easing function to a CSS linear() approximationconstcss=easingFnToCssLinear(bounce.out());// Create individual derivative functionsconstvelocity=createVelocityFn(bounce.out());constacceleration=createAccelerationFn(bounce.out());constjerk=createJerkFn(bounce.out());// Add initial velocity to any ease-inconstwithSlope=withV0(quadratic.in,0.5);// f'(0) = 0.5constanticipation=withV0(cubic.in,-0.3);// dips below 0// Derive a full family with initial velocityconstfamily=withV0Family({easeIn: quadratic.in,v0: 0.5});The package also exports lower-level utilities for specialized use cases:
- Spring conversion:
pennerToSpring,springToPenner— convert between Penner easing parameters and spring physics parameters - Bezier fitting:
fitCubicBezier— fit a cubic Bézier to an easing function - Heat map colors:
velocityToRgb,velocityToHslString,hslToRgb— color utilities for visualizing easing derivatives - Back helpers:
solveBackStrength,backEaseInVelocity - Spring helpers:
springPhysicsFirstPeak,amplitudeRatio,dampingRate,settlingPhaseCorrection - Expo helpers:
getExponentialEaseOutStartSlope,getExponentialEaseOutEndSlope,getExponentialEaseOutMetadata
@penner/classic-easing— the original Penner equations with classic naming conventions (easeOutQuad,easeInBounce, etc.)
If you're migrating from classic Penner easing functions:
// Old: easeOutBack(t, b, c, d, s)// New:constbackOut=back({overshoot: s*0.1}).out;// Convert strength to overshoot fractionconstresult=b+c*backOut(t/d);// Old: easeOutBounce(t, b, c, d)// New:constbounceOut=bounce.out;// Uses sensible defaultsconstresult=b+c*bounceOut(t/d);MIT - see LICENSE file for details.