Stable Exponential Smoothing Under Variable Framerate

The title might sound fancy, but this post is about a very practical thing that you might encounter quite often. A great way to smooth any kind of changing feature in your game is to use linear interpolation each frame to interpolate between the current value and the target value, like so:

myVar = lerp(myVar, target, 0.5)

It's so easy! And it creates a very nice, smooth transition between the old value of myVar and the new value, target. If you think about it for a bit, you'll realize that this thing is somehow exponential, because the rate at which myVar approaches target is obviously dependent on the difference between myVar and target. Hence the "exponential smoothing" in the title.

Now, there's a bit of a problem: where did I get the value 0.5? I could have just played with the constant until I was pleased with the rate of smoothing. But there's a bigger problem than arbitrary constants here: this code is highly framerate-dependent. Why? Well, if we're running at 60fps, then this interpolation happens 60 times in a second, which will obviously leave myVar a lot closer to target than if we were running at 10fps and the interpolation happened 10 times. The end result? You will notice things happening faster when your framerate is higher, even if you're using the proper time differential for physics. The problem is that we didn't account for the time differential when we wrote this function.

I'm not going to put the full derivation in this post, since my blog doesn't support Latex yet...but I will give you the answer to this problem:

myVar = lerp(myVar, target, 1. - pow(.5, dt))

What exactly is going on here? You can see that I'm now using dt, which is the time differential between frames (hence, we are taking into account framerate). But what of the pow and the .5? It turns out that the .5 now has a very precise meaning - it means that for every second that passes, .5 of the difference between myVar and target will decay. So after 3 seconds, myVar will have come 87.5% of the way to target (measuring from the initial value of myVar). This gives you a very easy way to think about the constant!

Not only is this a great little math trick to have up your sleeves, but it turns out that it is intimately related to the solution for making linear drag framerate-independent, which is something that you will not achieve if you just do the obvious discrete physics. I'll probably post on that later :)