Does anyone know the Formulas for Elastic Easing?

I use a native implementation of TweenService:GetValue to do animation easing because it is extremely fast compared to using the engine (about 7.5x faster).

However, I noticed a discrepancy between my elastic functions and TweenService’s. The curves are the same, but there are slight deviations from the “magic numbers”. Mine uses the math.pi constant while Roblox’s seems to use some estimate.

ElasticIn

Graph:

From: Easing Functions Cheat Sheet

Magic Number: (2 * math.pi) / 3 or 2.0943951023931953

Function:

local function ElasticIn(Value: number): number
	if Value == 0 then
		return 0
	end
	
	if Value == 1 then
		return 1
	end
	
	return -2 ^ (-10 + 10 * Value) * math.sin((-10.75 + Value * 10) * 2.0943951023931953)
end

ElasticOut

Graph:

From: Easing Functions Cheat Sheet

Magic Number: (2 * math.pi) / 3 or 2.0943951023931953

Function:

local function ElasticOut(Value: number): number
	if Value == 0 then
		return 0
	end
	
	if Value == 1 then
		return 1
	end
	
	return 1 + 2 ^ (-10 * Value) * math.sin((-0.75 + 10 * Value) * 2.0943951023931953)
end

ElasticInOut

Graph:

From: Easing Functions Cheat Sheet

Magic Number: (2 * math.pi) / 4.5 or 1.3962634015954636

Function:

local function ElasticInOut(Value: number): number
	if Value == 0 then
		return 0
	end
	
	if Value == 1 then
		return 1
	end
	
	if Value < 0.5 then
		return -2 ^ (-10 + 20 * Value) * math.sin((- 11.125 + 20 * Value) * 1.3962634015954636) * 0.5
	end
	
	return 1 + 2 ^ (10 + -20 * Value) * math.sin((-11.125 + 20 * Value) * 1.3962634015954636) * 0.5
end

You may need to zoom in on the graphs to see the divergence. I have tried other magic numbers (Archimedes constant, rounded estimates).

3 Likes

Sorry for bumping a year old post, but I recently discovered a practically identical formula for TweenService’s elastic easing. I’m posting my findings for any future developers that are in a similar predicament as OP.


Why is TweenService’s elastic easing different?

The elastic easing function by Robert Penner includes a period (p) and shift (s) variable, the standard implementation statically sets them as p = 0.3 and s = p/4.

Roblox’s TweenService implementation, however, seems to have s lerp between p/4 and p/6 based on the tween’s progress (alpha) and easing direction.


Formulas

Largest recorded error compared to TweenService:GetValue() is ~5e-7 (0.0000005) from 100,000 evenly spaced alpha samples.

if alpha <= 0 then return 0 end
if alpha >= 1 then return 1 end

local p = 0.3
local c = 2 * math.pi / p

-- ElasticIn
local s = (2 + alpha) * p / 12 -- (p/6) -> (p/4) lerped by alpha
return -2 ^ (10 * (alpha - 1)) * math.sin((alpha - 1 - s) * c)

-- ElasticOut
local s = (3 - alpha) * p / 12 -- (p/4) -> (p/6) lerped by alpha
return 2 ^ (-10 * alpha) * math.sin((-alpha - s) * c) + 1

-- ElasticInOut
local s = (1.5 - math.abs(alpha - 0.5)) * p / 6 -- (p/6) -> (p/4) -> (p/6) lerped by alpha
return if alpha < 0.5
	then -2 ^ (10 * (2 * alpha - 1)) * math.sin((2 * alpha - 1 - s) * c) * 0.5
	else 2 ^ (-10 * (2 * alpha - 1)) * math.sin((2 * alpha - 1 + s) * c) * -0.5 + 1
4 Likes

Thank you so much for this!

I had originally gotten closer functions by looking at some old engine code, but yours is much more precise and performant.

1 Like