npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@penner/easing

v0.3.0

Published

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

Readme

@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 }
const bounceOut = bounce({ bounces: 4, decay: 0.95 }).out;
const backOut = back({ overshoot: 0.15 }).out;
const springOut = physicsSpring({ bounces: 4, decay: 0.9 }).out;

// Defaults are also available as properties — no call needed
const defaultBackOut = back.out;

// Use in animations
const progress = 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 families
const sqrt = power({ exponent: 0.5 }); // Square root easing
const custom = power({ exponent: 2.5 }); // t^2.5 easing

const sqrtOut = sqrt.out;
const customIn = custom.in;

// All variants
const fam = power({ exponent: 1.7 });
const easeIn = fam.in;
const easeOut = fam.out;
const easeInOut = fam.inOut;
const easeOutIn = 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 (exponent: 3, overshoot: 0.1) — equivalent to classic Back
const easeIn = flex.in;
const easeOut = flex.out;

// Custom: stronger anticipation with a steeper acceleration
const snappy = flex({ exponent: 4, overshoot: 0.2 });
const snappyOut = snappy.out;
const snappyInOut = snappy.inOut;
interface FlexConfig {
  exponent: 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); exponent: 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 directly
const easeOut = back.out;

// Custom overshoot (20%)
const easeOutBig = back({ overshoot: 0.2 }).out;

// All variants
const easeIn = back({ overshoot: 0.15 }).in;
const easeInOut = back({ overshoot: 0.1 }).inOut;
const easeOutIn = 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)
const easeOut = bounce.out;

// Custom bounce
const easeOutBouncy = bounce({
  bounces: 6,
  decay: 0.8,
}).out;

// All variants
const easeIn = bounce({ bounces: 3, decay: 0.9 }).in;
const easeInOut = bounce.inOut;
const easeOutIn = 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% decay
const easeOut = overBounce.out;

// Higher overshoot
const easeOutBig = overBounce({ overshoot: 0.5, bounces: 4 }).out;

// Specify initial velocity instead of overshoot
const easeOutFast = overBounce({ v0: 4.0, bounces: 2 }).out;

// All variants
const easeIn = overBounce.in;
const easeInOut = overBounce.inOut;
const easeOutIn = overBounce.outIn;

Spring

Damped oscillations with configurable bounces and decay.

import { physicsSpring } from "@penner/easing";

// Default spring (4 bounces, 95% decay)
const easeOut = physicsSpring.out;

// Custom oscillations
const easeOutCustom = physicsSpring({
  bounces: 6,
  decay: 0.9,
}).out;

// Critically damped (no oscillation)
const easeOutSmooth = physicsSpring({ bounces: 0 }).out;

// All variants
const easeIn = physicsSpring.in;
const easeInOut = physicsSpring.inOut;
const easeOutIn = 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)
const easeOut = overdampedSpring.out;

// Sharper arrival (closer to critical damping)
const sharp = overdampedSpring({ overdamping: 0.2 }).out;

// Languid, near-linear approach
const heavy = overdampedSpring({ overdamping: 0.85 }).out;

// Endpoint delegations:
const critical = overdampedSpring({ overdamping: 0 }).out; // critically damped
const linear = overdampedSpring({ overdamping: 1 }).out; // linear easing

// All variants
const easeIn = overdampedSpring.in;
const easeInOut = overdampedSpring.inOut;
const easeOutIn = 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)
const easeOut = innerSpring.out;

// More oscillations
const easeOutBouncy = innerSpring({ bounces: 3 }).out;

// Custom decay
const easeOutCustom = innerSpring({ bounces: 2, decay: 0.5 }).out;

// All variants
const easeIn = innerSpring.in;
const easeInOut = innerSpring.inOut;
const easeOutIn = 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)
const easeOut = outerSpring.out;

// More oscillations
const easeOutBouncy = outerSpring({ bounces: 3 }).out;

// Less aggressive decay
const easeOutCustom = outerSpring({ bounces: 2, decay: 0.5 }).out;

// All variants
const easeIn = outerSpring.in;
const easeInOut = outerSpring.inOut;
const easeOutIn = 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)
const quadOut = quadratic.out;
const cubicIn = cubic.in;
const quartInOut = quartic.inOut;
const sineOutIn = 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)
const circ = superellipse({ exponent: 2 });

// Softer than circular
const soft = superellipse({ exponent: 1.5 });

// Sharper, more squared-off
const sharp = superellipse({ exponent: 4 });

// All variants
const easeIn = sharp.in;
const easeOut = sharp.out;
const easeInOut = sharp.inOut;
const easeOutIn = sharp.outIn;
interface SuperellipseConfig {
  /** Superellipse exponent (1 = linear, 2 = circular, →∞ = square) */
  exponent: number;
}

Linear, Smoothstep, Smootherstep, Smootheststep

Simple easing functions exported as single EasingFn values (not families).

import { linear, smoothstep, smootherstep, smootheststep } from "@penner/easing";

const value = smoothstep(0.5); // C¹ Hermite (degree 3)
const value2 = smootherstep(0.5); // C² (degree 5)
const value3 = 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) ≡ smootheststep
const custom = smoothstepN(4); // degree 9, C⁴ smooth

Exponential

Configurable exponential easing with utility functions.

import { exponential, makeExponentialEaseOut } from "@penner/easing";

// Standard exponential family
const easeOut = exponential.out;
const easeIn = exponential.in;

// Custom exponential ease-out
const customExpo = 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 families
import { bounce, physicsSpring } from "@penner/easing";

// Import standard easings alongside physics-based ones
import { quadratic, cubic, back } from "@penner/easing";

Configuration Interfaces

BounceConfig

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

SpringPhysicsConfig

interface SpringPhysicsConfig {
  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 need
const { easing, velocity } = easingKit({ easingFn: physicsSpring.out() });

// Use easing for position, velocity for scale
element.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 API
element.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 derivatives
const kit = easingKit({ easingFn: spring.out() });
kit(0.5); // call as a plain easing function
kit.velocityFn(0.5); // numerical 1st derivative
kit.accelerationFn(0.5); // numerical 2nd derivative

You can supply exact analytical derivatives when the particular math formulas are known:

const kit = easingKit({
  easingFn: (t) => t * t,
  velocityFn: (t) => 2 * t, // exact derivative of t²
});

EasingKit types

interface EasingKitOptions {
  easingFn: EasingFn;
  velocityFn?: VelocityFn; // analytical 1st derivative
  accelerationFn?: AccelerationFn; // analytical 2nd derivative
  jerkFn?: JerkFn; // analytical 3rd derivative
  meta?: EasingKitMeta; // factory name + args for serialization
}

type EasingKit = EasingFn & {
  readonly easingFn: EasingKit; // self-reference for destructuring
  readonly easing: CSSEasing; // CSS linear() string
  readonly velocityFn: VelocityFn;
  readonly accelerationFn: AccelerationFn;
  readonly jerkFn: JerkFn;
  readonly velocity: CSSEasing; // velocity as CSS linear()
  readonly meta: EasingKitMeta;
  readonly toString: () => 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)
const myEaseIn = reverseEasingFn(quadratic.out);

// Mirror an easing to the right (ease-in becomes ease-in-out)
const myEaseInOut = mirrorEasingFnToRight(quadratic.in);

// Clamp values to 0-1 range
const safe = clamp01(someValue);

// Convert an easing function to a CSS linear() approximation
const css = easingFnToCssLinear(bounce.out());

// Create individual derivative functions
const velocity = createVelocityFn(bounce.out());
const acceleration = createAccelerationFn(bounce.out());
const jerk = createJerkFn(bounce.out());

// Add initial velocity to any ease-in
const withSlope = withV0(quadratic.in, 0.5); // f'(0) = 0.5
const anticipation = withV0(cubic.in, -0.3); // dips below 0

// Derive a full family with initial velocity
const family = 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.)
  • @penner/responsive-easing — dynamically fuses head/tail easing curves with C¹ continuity, built on top of this package

Migration from Legacy Penner Functions

If you're migrating from classic Penner easing functions:

// Old: easeOutBack(t, b, c, d, s)
// New:
const backOut = back({ overshoot: s * 0.1 }).out; // Convert strength to overshoot fraction
const result = b + c * backOut(t / d);

// Old: easeOutBounce(t, b, c, d)
// New:
const bounceOut = bounce.out; // Uses sensible defaults
const result = b + c * bounceOut(t / d);

License

MIT - see LICENSE file for details.