Skip to main content
GDN8 PATIO

Implementing Easing Functions and Applying Them to Progress

This note assumes a basic understanding of CSS and GSAP implementations and organizes the differences among representative types and how to use them in JavaScript.

Implementation in JavaScript

Treat scroll amount or elapsed time as progress from 0 to 1 and pass it to an easing function.

This is an implementation for cases where GSAP cannot be introduced or where you only use the easing calculation. Payload size is always a concern, so it comes up more often than expected.

The names follow GSAP Eases so they map easily to everyday production work. power1 through power4 are GSAP-specific names. The curves themselves are ordinary polynomial easings: power1 is Quad (quadratic), power2 is Cubic (cubic), power3 is Quart (quartic), and power4 is Quint (quintic). GSAP groups them under the shared name Power so that higher degrees produce stronger change.

const EasingIn = {
	linear: t => t,
	power1: t => t ** 2,
	power2: t => t ** 3,
	power3: t => t ** 4,
	power4: t => t ** 5,
	sine: t => 1 - Math.cos( t * Math.PI / 2 ),
	expo: t => t === 0 ? 0 : 2 ** ( 10 * t - 10 ),
	circ: t => 1 - Math.sqrt( 1 - t ** 2 ),
	back( t ) {
		const overshoot = 1.70158;

		return ( overshoot + 1 ) * t ** 3 - overshoot * t ** 2;
	}
};

function easing( type, direction, t ) {
	switch ( direction ) {
		case 'in':
			return EasingIn[ type ]( t );
		case 'out':
			return 1 - EasingIn[ type ]( 1 - t );
		case 'inOut':
			return t < 0.5
				? EasingIn[ type ]( t * 2 ) / 2
				: 1 - EasingIn[ type ]( ( 1 - t ) * 2 ) / 2;
		default:
			throw new Error( `Unknown easing direction: ${ direction }` );
	}
}

easing() takes the type as the first argument, the direction as the second, and the progress as the third. Out inverts the input and output. In Out applies In to the first half and an inverted In to the second half. The branch in Expo keeps the start value exactly 0. This does not reproduce GSAP’s internal implementation. When GSAP is available and you need the curves to match exactly, use something like gsap.parseEase( 'expo.out' ).

Building a value from progress

Ease a progress value from scroll amount, drag amount, and so on to produce a value.
The following example gets value when progress is 0.4.

const progress = 0.4;
const value = easing( 'power1', 'inOut', progress );
console.log( value );

When progress is 0, value is 0. When it is 1, value is 1. Types such as Back that overshoot can go outside this range, so use lerp() when mapping value to another range such as 0 to 100.

function lerp( start, end, progress ) {
	return start + ( end - start ) * progress;
}
const progress = 0.4;
const value = lerp( 0, 100, easing( 'power1', 'inOut', progress ) );
console.log( value );

In this case, when progress is 0, value is 0. When it is 1, value is 100.

You can check the motion in the easing from progress demo.

Building progress from elapsed time

Next is an example that logs a Power1 In Out value that changes from 0 to 1 over 1200ms.

const startedAt = performance.now();
function update( now ) {
	const progress = Math.min( ( now - startedAt ) / 1200, 1 );
	console.log( easing( 'power1', 'inOut', progress ) );
	if ( progress < 1 ) requestAnimationFrame( update );
}

update( startedAt );

progress is elapsed time divided by 1200ms. Passing it through Power1 In Out makes the increase small near the start and end and larger around the middle.

Easing in CSS

By registering approximate easings with CSS cubic-bezier, you can keep JS animations aligned. They are also useful in scenes that do not use JS. You can check the curves and the actual motion in the easing comparison demo.

:root {
	--ease-none: linear;
	--ease-power1-in: cubic-bezier(0.333333, 0, 0.666667, 0.333333);
	--ease-power1-out: cubic-bezier(0.333333, 0.666667, 0.666667, 1);
	--ease-power1-in-out: cubic-bezier(0.455, 0.03, 0.515, 0.955);
	--ease-power2-in: cubic-bezier(0.333333, 0, 0.666667, 0);
	--ease-power2-out: cubic-bezier(0.333333, 1, 0.666667, 1);
	--ease-power2-in-out: cubic-bezier(0.645, 0.045, 0.355, 1);
	--ease-power3-in: cubic-bezier(0.5, 0, 0.75, 0);
	--ease-power3-out: cubic-bezier(0.25, 1, 0.5, 1);
	--ease-power3-in-out: cubic-bezier(0.76, 0, 0.24, 1);
	--ease-power4-in: cubic-bezier(0.64, 0, 0.78, 0);
	--ease-power4-out: cubic-bezier(0.22, 1, 0.36, 1);
	--ease-power4-in-out: cubic-bezier(0.83, 0, 0.17, 1);
	--ease-sine-in: cubic-bezier(0.12, 0, 0.39, 0);
	--ease-sine-out: cubic-bezier(0.61, 1, 0.88, 1);
	--ease-sine-in-out: cubic-bezier(0.37, 0, 0.63, 1);
	--ease-expo-in: cubic-bezier(0.7, 0, 0.84, 0);
	--ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);
	--ease-expo-in-out: cubic-bezier(0.87, 0, 0.13, 1);
	--ease-circ-in: cubic-bezier(0.55, 0, 1, 0.45);
	--ease-circ-out: cubic-bezier(0, 0.55, 0.45, 1);
	--ease-circ-in-out: cubic-bezier(0.85, 0, 0.15, 1);
	--ease-back-in: cubic-bezier(0.36, 0, 0.66, -0.56);
	--ease-back-out: cubic-bezier(0.34, 1.56, 0.64, 1);
	--ease-back-in-out: cubic-bezier(0.68, -0.6, 0.32, 1.6);
	--ease-steps: steps(9, jump-none);
	/* bounce / elastic are reproduced by sampling with linear() */
}

In and Out for power1 and power2 can be replaced with cubic Beziers as the same curves. In Out switches formulas partway through, so a single cubic-bezier() is an approximation. power3 and power4 are also approximations because their degrees exceed cubic. Bounce and Elastic can be converted to CSS by evaluating the function from gsap.parseEase() at equal intervals.

function easeToLinear( name, points = 80 ) {
	const ease = gsap.parseEase( name );
	const values = [];

	for ( let index = 0; index <= points; index++ ) {
		values.push( Number( ease( index / points ).toFixed( 4 ) ) );
	}

	return `linear(${ values.join( ', ' ) })`;
}

More points get closer to the original curve. CSS also gets longer, so decide based on the target browsers, how fine the curve needs to be, and the playback duration.

Eases beyond the above

When the standard types are not enough, there are also these Eases.

Type Package Use
RoughEase EasePack Irregular shake or noise
SlowMo EasePack Show only the middle section slowly
ExpoScaleEase EasePack Bring scale closer to a visually constant speed
CustomEase Plugin Build an arbitrary curve from an SVG path or control points
CustomBounce Plugin Adjust bounce strength and squash
CustomWiggle Plugin Adjust oscillation count and how it shakes

Deciding direction and strength with Core types first, then using additional Eases only for motion that those cannot express, is easier to manage.

References

  • ※ Descriptions related to GSAP and CSS are based on the official documentation and GSAP 3.15.0 as of July 22, 2026.
  • ※ This article is an AI translation of the Japanese original.