Skip to content

Repository files navigation

Mirror of a package developed in a private monorepo — issues and pull requests are welcome here.

@penner/easing

Modern TypeScript implementations of the classic Penner easing functions with physics-based parameters and tree-shakeable exports.

npm version

Features

  • 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

Installation

npm install @penner/easing

Quick Start

import{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.5

Easing Families

Power

Custom 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;

Flex

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.

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;

Bounce

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;

OverBounce

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;

Spring

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

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).

InnerSpring

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;

OuterSpring

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;

Standard Easings

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;

Superellipse

Generalizes circular easing via the Lamé curve $|x|^n + |y|^n = 1$. The exponent controls the curve shape: 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;}

Linear, Smoothstep, Smootherstep, Smootheststep

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⁴ smooth

Exponential

Configurable 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);

Force-Based Easings (Experimental)

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.

Tree-Shaking

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";

Configuration Interfaces

BounceConfig

interfaceBounceConfig{bounces?: number;// Number of bounces (default: 4)decay?: number;// Total height decay as fraction 0-1 (default: 0.95)}

SpringPhysicsConfig

interfaceSpringPhysicsConfig{bounces?: number;// Visible oscillation half-cycles (default: 4)decay?: number;// Total amplitude decay as fraction 0-1 (default: 0.95)}

EasingKit

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 derivative

You 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²});

EasingKit types

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;};

Utility Functions

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});

Advanced

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

Related Packages

  • @penner/classic-easing — the original Penner equations with classic naming conventions (easeOutQuad, easeInBounce, etc.)

Migration from Legacy Penner Functions

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);

License

MIT - see LICENSE file for details.

About

Modern TypeScript implementations of the classic Penner easing functions with physics-based parameters.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages