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
- Easing Comparison – Garden Eight
- Easing – GSAP
- GSAP Core source
- CSS Easing Functions Module Level 2 – W3C
- ※ 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.