The friction Dave describes where changing one param effects all the other params in an animation brought to mind this section of an old Bret Victor talk: https://www.youtube.com/watch?v=a-OyoVcbwWE&t=716s
Bouncy animations that overshoot just seem like a bad idea in general. The purpose of a UI animation is to guide the eye, but the bounce explicitly introduces a reversal of motion at the end before stopping.
Easing functions are just very cargo culty. We've had the same basic set that dates from the Flash era. Now there's an Apple variant that's just a parametric version of the same idea, but it lacks guaranteed continuity and it's even harder to control?
Personally I've had far better results using the repeated-lerping-towards-a-target trick, aka a true exponential ease. When stacked, you get a classic LTI (linear time invariant) system, and the math around how those behave is well established.
Classic hand-drawn animation does often use stretching and squeezing to emphasize and to create a sense of anticipation, but that's very different and always dependent on the specific motion. You can't automate that by making everything act like jello.
Overshoot is important for scroll views. Without the bounce, there is no feedback if you have scrolled to the edge or not. Early Android lacked the bounce, and it would invariably lead to users scrolling again to be sure they had indeed scrolled to the edge of the view.
function sprung_response(t,pos,vel,k,c,m)
local decay = c/2/m
local omega = math.sqrt(k/m)
local resid = decay*decay-omega*omega
local scale = math.sqrt(math.abs(resid))
local T1,T0 = t , 1
if resid<0 then
T1,T0 = math.sin( scale*t)/scale , math.cos( scale*t)
elseif resid>0 then
T1,T0 = math.sinh(scale*t)/scale , math.cosh(scale*t)
end
local dissipation = math.exp(-decay*t)
local evolved_pos = dissipation*( pos*(T0+T1*decay) + vel*( T1 ) )
local evolved_vel = dissipation*( pos*(-T1*omega^2) + vel*(T0-T1*decay) )
return evolved_pos , evolved_vel
end
For anticipation, just add an extra initial velocity in the opposite direction and let the closed-form solution handle the time evolution. The main trick here is to keep both position and velocity as state. There is no need to “step through the simulation”.
Material Design 3's "motion physics system" uses damped harmonic oscillators, too. The parameters (undamped angular frequency omega0 and damping ratio zeta) they use are on this page:
To me this article is ridiculous bcause the first thing I think of when I hear "easing function" is the css version of that concept, which seems to solve all the authors problems? The css easing functions are weirdly limited, because they consist of a single function that takes 4 parameters, then a bunch of specializations of that function. There are great websites on the internet for configuring those parametere though, and they are quite general from a practical standpoint. It should be trivial to emulate the concept in any other language/system.
Oscullation/"anticipation" in transitions is so gimmicky and cartoonish. Living things don't move like that, and mechanical things don't unless they are broken, chintzy, or poorly designed (underdamped).
In my view we most certainly do, though of course not all the time.
Oscillations typically happen when moving quickly and then stopping at a certain position.
A prime example is the ISSF 25m rapid fire pistol event, where you start with the pistol pointing down at 45 degrees, and within 8, 6 and 4 seconds have to raise the pistol and take 5 aimed shots.
Beginners will often find they oscillate vertically above and below the center of the target as they raise the pistol, and it usually takes a fair bit of practice not to oscillate. This is more pronounced at the rapid pistol events due to the tight time constraint forcing them to move quickly, especially at the final 4 second round.
Another example is gymnastic rings, especially when they go from a swinging motion to stopping vertically or horizontally.
Anticipation-like movement often occurs when we want to get a bit of extra momentum. For example, casually tossing a base ball or tennis ball often results in a bit of backwards movement first, before the main swing. Typically it's one smooth movement.
The correct way to do "anticipation" is to apply it as something is being pressed, then finish the animation on release. Putting both together in a single easing function is missing the point, IMO.
Apple's easing function (like others) is parameterized by physical characteristics (e.g. spring force, damping) that are easier to model from first principles, but there are other parameters (overshoot distance, anticipation size, animation time) that are more useful for animation. A closed-form parametrization with the latter might be tricky to derive, but some kind of iterative solver (plug in desired animation parameters, get values for the physical parameters) shouldn't be too difficult?
As an engineer I'm attracted towards the PID based one, but also wary of the amount of CPU cycles used just for a little animation. It seems like overkill, for some reason.
The section on feedback control reminded me of procedural animation where you don't calculate the velocities and positions directly; instead the animation is a consequence of constraints and a target.
I took your PD controller concept and added anticipation using two targets: the original mouse target, and an "anticipation" target set proportionally based on the distance from the point to the main target[1].
This also made me think of Jelly Car and the amazing simulations they did using rigid frames for soft bodies, called Shape Matching[2]. Instead of simulating the soft body physics directly, they used a frame, springs, and "gas" to constrain points, which moved towards targets fixed to the frame.
Honestly, I think once you are tired of easing functions you are best off going to real physics. (By which I mean a simple Verlet integrator).
I've posted this before for other reasons but https://www.luduxia.com/showdown/ - in this case everything is easing functions, except for, bizarrely, the giant text that announces win/draw and the text inside the counter, which is done with actual physics because that proves better, especially with constantly moving targets.
I also didn't like when a new easing happened _while_ another easing was happening, which often felt very jerky. Had to do a bunch of calculus (derivatives) by hand and wrote a small library for it in JS:
> maybe I can start by just writing up my rant, and maybe someone else will read this and find a Research Project in here and will make the time before me
This is one of the things I find fascinating about Bret Victor. In the early 10s, he was giving a lot of really influential talks on creative coding. Someone asked him what his motivation was. He essentially said that he gives his ideas away, because he wants to live in a world where they exist. It's easy enough to build enough of a demo to give a talk, but he wanted someone else to do the legwork to grow it into a full project.
imo there just need to be a few more default easing functions, the standard ones aren't quite natural enough. There must be a standard curve equivalent to the "human pushing/pulling an object from A->B" function that would actually look natural. EaseIn/EaseOut/etc is never quite it; human motion has a bit of higher-order feedback used to regulate it. Maybe to do with the combination of: all the motion happens via muscular springs, plus, there's a well-trained system that regulates the motion to minimize inefficient use of energy. Whether I jerk suddenly and then stop it, or move it slowly so it smoothly decelerates to the final point, it never looks as mechanical as the stuff you see in code.
I have found myself leaning heavily on easing functions to smooth motion within my terminal visual effects engine. I do not have a very strong math background so I am limited in how much I can modify the commonly available functions. I have found it useful to create custom easing functions by mapping the easing function progress across a bezier curve. There's an example in the changeblog write-up from the last release:
Interesting article. But why is it an issue for it to be iterative?
I would imagine you're going to step through time anyways, to draw frame after frame. Wouldn't it in fact be cheaper to step through using some finite differences scheme, with one FD step for one draw step? It could be as cheap as a handful of additions per time step. Unless I'm overlooking something?
If needed for stability or accuracy, you can also step through several FD steps in one frame, but I'd imagine at a rate of 20fps, this should not be necessary for the types of equations considered.
Basically any transfert function that is used as interpolator can also be used as "easing". E.g a quadratic Bézier (let's say with empirically determined coeffs). One lesser known I used to like much is the super ellipse, although very costly.
Why does literally every single article on easing functions mention Penner? First off he didn’t invent them. Games had been using those functions since at least the 80s.
Second, why only him and these functions? I don’t write: In JavaScript by Brendan Eich using Node.js by Ryan Dahl I installed a package using npm by Isaac Z. Schlueter called React by Jordan Walke and for the backend I used TJ Holowaychuk’s express.js. Instead just write: In JavaScript using node.js I installed react and express.js
But literally, I’ve never read an article about easing functions that just says “easing functions”. they all feel obligated to mention Penner” Why? and no, it’s not because open source or multiple contributors. I used those examples because I can’t name the 1000s of people for functions which is precisely my point
I have the feeling that B-splines would be a good solution for this problem. Given that they have a continuous zeroth (i.e., the function is continuous), first, and second derivative, the motion will always be smooth and there will be no kinks. However, maybe it's moving the problem because now you must tune the coefficients of the B-spline instead of damping parameters (even though a direct mapping between these must exist but this mapping may not be trivial).
Can anyone comment on easing functions vs constant on a longer period of time, would they be distinictive or seem the same on a long and smooth enough slope
Distinctive. Easing functions usually force the beginning and end to be noticeably different, and throughout the middle portion they're basically identical to constant velocity. The discontinuity at the start and stop are what easing functions are most concerned with.
In the limit, constant velocity still has the same discontinuity (infinite acceleration) at those points, and easing functions generally stick with the same constant-ish acceleration at those points. Easing makes it feel like the object has to get up to speed, and it takes about the same amount of time regardless of distance/overall time.
32 comments
[ 5.6 ms ] story [ 81.1 ms ] threadEasing functions are just very cargo culty. We've had the same basic set that dates from the Flash era. Now there's an Apple variant that's just a parametric version of the same idea, but it lacks guaranteed continuity and it's even harder to control?
Personally I've had far better results using the repeated-lerping-towards-a-target trick, aka a true exponential ease. When stacked, you get a classic LTI (linear time invariant) system, and the math around how those behave is well established.
Classic hand-drawn animation does often use stretching and squeezing to emphasize and to create a sense of anticipation, but that's very different and always dependent on the specific motion. You can't automate that by making everything act like jello.
https://codepen.io/Trung0246/pen/jxvvzY
https://www.desmos.com/calculator/mu80ttc9aa
For anticipation, just add an extra initial velocity in the opposite direction and let the closed-form solution handle the time evolution. The main trick here is to keep both position and velocity as state. There is no need to “step through the simulation”.https://m3.material.io/styles/motion/overview/how-it-works
Oscillations typically happen when moving quickly and then stopping at a certain position.
A prime example is the ISSF 25m rapid fire pistol event, where you start with the pistol pointing down at 45 degrees, and within 8, 6 and 4 seconds have to raise the pistol and take 5 aimed shots.
Beginners will often find they oscillate vertically above and below the center of the target as they raise the pistol, and it usually takes a fair bit of practice not to oscillate. This is more pronounced at the rapid pistol events due to the tight time constraint forcing them to move quickly, especially at the final 4 second round.
Another example is gymnastic rings, especially when they go from a swinging motion to stopping vertically or horizontally.
Anticipation-like movement often occurs when we want to get a bit of extra momentum. For example, casually tossing a base ball or tennis ball often results in a bit of backwards movement first, before the main swing. Typically it's one smooth movement.
I took your PD controller concept and added anticipation using two targets: the original mouse target, and an "anticipation" target set proportionally based on the distance from the point to the main target[1].
This also made me think of Jelly Car and the amazing simulations they did using rigid frames for soft bodies, called Shape Matching[2]. Instead of simulating the soft body physics directly, they used a frame, springs, and "gas" to constrain points, which moved towards targets fixed to the frame.
[1]: https://editor.p5js.org/Thomascountz/sketches/YXWm_VV6s
[2]: https://www.gamedeveloper.com/programming/deep-dive-the-soft...
I've posted this before for other reasons but https://www.luduxia.com/showdown/ - in this case everything is easing functions, except for, bizarrely, the giant text that announces win/draw and the text inside the counter, which is done with actual physics because that proves better, especially with constantly moving targets.
https://github.com/franciscop/ola
Note: ola means (sea) wave in Spanish
This is one of the things I find fascinating about Bret Victor. In the early 10s, he was giving a lot of really influential talks on creative coding. Someone asked him what his motivation was. He essentially said that he gives his ideas away, because he wants to live in a world where they exist. It's easy enough to build enough of a demo to give a talk, but he wanted someone else to do the legwork to grow it into a full project.
https://chrisbuilds.github.io/terminaltexteffects/changeblog...
I would imagine you're going to step through time anyways, to draw frame after frame. Wouldn't it in fact be cheaper to step through using some finite differences scheme, with one FD step for one draw step? It could be as cheap as a handful of additions per time step. Unless I'm overlooking something?
If needed for stability or accuracy, you can also step through several FD steps in one frame, but I'd imagine at a rate of 20fps, this should not be necessary for the types of equations considered.
Second, why only him and these functions? I don’t write: In JavaScript by Brendan Eich using Node.js by Ryan Dahl I installed a package using npm by Isaac Z. Schlueter called React by Jordan Walke and for the backend I used TJ Holowaychuk’s express.js. Instead just write: In JavaScript using node.js I installed react and express.js
But literally, I’ve never read an article about easing functions that just says “easing functions”. they all feel obligated to mention Penner” Why? and no, it’s not because open source or multiple contributors. I used those examples because I can’t name the 1000s of people for functions which is precisely my point
In CSS, there's a long standing feature request to add a spring() timing function: https://github.com/w3c/csswg-drafts/issues/280
This would not only be great for developer ergonomics, but would remove JS from the animation path for these cases.
Er ... "un ... eased"? :)
https://medium.com/hackernoon/the-spring-factory-4c3d988e712...
And a bounce function:
https://medium.com/hackernoon/the-bounce-factory-3498de1e526...
In the limit, constant velocity still has the same discontinuity (infinite acceleration) at those points, and easing functions generally stick with the same constant-ish acceleration at those points. Easing makes it feel like the object has to get up to speed, and it takes about the same amount of time regardless of distance/overall time.