It's Christmas and I'm surely not suppose to be writing stuff at 2AM but here I am lol.

So I stumbled upon an unpopular part of the web lately and it's been from one 'WTF' to another. After a few hours of exploring various articles, videos and codepens, trying to understand in-depth concepts of web animations, I'm currently sitting on my new, comfortable chair trying to think of the best words for these paragraphs but the recursive thought running through my head is simple - I need to get better real quick".

I'll be explaining a few jargons I managed to pick up so far but here's an disclaimer - This article just compiles a number of basic stuff I learnt or think is true about these concepts so there may be some incorrect assumptions here and there but overall, it's honest work I guess.

window.requestAnimationFrame - What Is This? What Does It Do?

If you're not a big fan of moving stuff around frequently on a web page, you probably may have seen this method somewhere before now but then it turned out to be one of those unnecessary things in javascript that you don't need to clog your head with. I had the same thought as well until recently.

Keeping things simple, you can think of window.requestAnimationFrame as a setInterval method but specially optimised for your computer/smartphone and when called, runs multiple times within one second. Now let me explain further...

Let's say you have a function that you wish to call repeatedly every second. That's easy, we'll just use a normal setInterval. The setInterval will call the function every second and this continues infinitely till the tab/browser window is closed.

In order to understand properly what requestAnimationFrame does, let's imagine we have a div on our page and we wish to move this div to the right by 500px; In that case, we'll simply write a function that adds transform: translateX(500px) to the style property of the div, and then call the function once. Easy right?

Yeah, but it could be better, let's animate the movement of our div from A to point B by gradually moving the div to the right until it gets to its final point. We can achieve this by updating the translateX by a few pixels at an interval until it gets to its final destination using setInterval.

//javascript const ourDiv = document.querySelector("div");
let position = 0;

const myFunction = () => {
  position += 1;
  ourDiv.style.transform = `translateX(${position}px)`;

  if (position >= 500) {
    position = 0;
  }
};

const interval = setInterval(() => {
  myFunction();
}, 100);

I should embed a codepen here but their iframe doesn't look great on my blog so here's a link if you wish to see this animation live

One thing you should take note of is the interval we used and also the value by which we increment the position each time our function runs. The smaller the value of the interval, the smoother our animation gets and the larger the value of our increment, the faster the animation goes.

In this case, we're updating the position of our div by 1px 10 times in one second (100ms interval) hence creating an animated visual of the div from point A to point B and I could say that this is basically how videos work. Think of a video as a collection of images/snapshots taken at intervals and then played in a sequence, just like flipbook animations.

Now if you checked the codepen I shared earlier and the flipbook video link above, you'll see that both of them appear to be jumpy and not-smooth, this is because of the rate at which the frames are updated. In videography, this phenomenon is known as frame rate or FPS (frames per seconds). This is why when buying a new iPhone or Camera, you check and see something like 'can shoot at 30FPS or supports HD video at 60FPS'. What this basically means is, when taking a video with the camera, it captures 60 frames/shots/pictures per second and the plays these frames in a sequence to produce a video.

For this reason, cameras with higher FPS produce better quality videos because no frames are skipped and it captures every moment hence producing a smooth, less jumpy video.

Oh we're talking about cameras now and not javascript? Nah, here comes the interesting part.

So, putting all these I just said together, we need to make our animated div less jumpy and we can achieve this by increasing the FPS. Our current frame rate from the snippet above is about 10FPS which simply means we are updating our div 10 times per second. If we reduce the interval to 50ms, we'll have a frame rate of 20FPS which will give us a smoother animation.

Knowing that higher frame rate gives us better animations, we could just decide to be clever and use an interval of say, 5ms. This will give us an FPS of 200 and then our animation will be smoother and faster (a little too fast actually), but for a number of reasons which I won't be covering in this article, it takes a toll on the performance or our web page. Imagine calling a function 200 times in one second, a little too much right? This is where window.requestAnimationFrame comes in!

The window.requestAnimationFrame() method works just like our setInterval function up there but the difference is, it calls our animation function with a frame rate that matches the device monitor/screen specifications. Most monitors have a refresh rate of 60Hz, some high quality ones have over 100Hz and some even up to 240Hz.

A monitor's refresh rate is simply the maximum of times in one second an object can get updated on screen. So what requestAnimationFrame does is, it updates our animation in a rate per second that matches the refresh rate of whatever monitor we're viewing the webpage on.

This gives us the smoothest animation possible on that monitor while maintaining maximum performance. Also note that calling window.requestAnimationFrame() executes the function you put in it only once so we'll have to create a loop in order to continuously update our animation by adding another requestAnimationFrame() callback inside our function.

//javascript const ourDiv = document.querySelector("div");
let position = 0;

const myFunction = () => {
  position += 1;
  ourDiv.style.transform = `translateX(${position}px)`;
  
  if (position >= 500) {
    position = 0;
  }
  
  window.requestAnimationFrame(myFunction);
};

window.requestAnimationFrame(myFunction);

There you have it, requestAnimationFrame being useful. Now, in our code up here, the speed of our animation now depends on how many pixels you add to the position each time our function runs. Currently, our div moves from position A - B in a linear manner and this is totally fine but then we can decide to add some form of easing/smooth effects in our animation. This is where we get to make use of linear interpolation.

WTF is Linear Interpolation?

A linear interpolation function often called LERP is a function that takes in three values - start, end and percentage, and then returns a point between the start and end values based on the percentage. That's a little confusing but chile, I'll explain, here's an example.

//javascript const lerp = (start, end, percentage) => {
  // get the difference between start & end values
  const differenceBetween = end - start;

  // get a percentage of that value
  const lerpedValue = differenceBetween * percentage;

  // return the value of the calculated percentage
  return lerpedValue
};

// calling lerp with start = 0, end = 100 and percentage = 50%
lerp(0, 100, 0.5);

The function above takes in a starting point - 0 and final point - 100 and then returns a point that is 50% in between 0 and 100 which is obviously 50.

Okay, what's that gotta do with me? Chile, I'll explain.

Instead of increasing the position of our div by a constant 1px each time our function gets called to produce a linear animation, using a lerp function to interpolate between the current div position and the final position will produce a very smooth easing visual that slows our div down as it draws closer to its final destination.

Take for example, instead of using position += 1 in our animation function above (the one we used requestAnimationFrame) we instead decide to use position += lerp(position, 500, 0.1), the value of position will be equal to 50 when our function runs for the first time, then the next time our function runs the value of position will be smaller than 50 because the distance between starting point (0) and ending point (500) has changed by 10%.

Our new starting point is now 50 while our ending point remains the same (500), so the next time our function runs, the number of pixels by which we'll be moving our div will be 10% of [500 - 50] which is 45 (10% of 450).

//javascript const ourDiv = document.querySelector("div");
let position = 0;

const myFunction = () => {
  position += lerp(position, 500, 0.1);
  ourDiv.style.transform = `translateX(${position}px)`;
  
  if (position >= 500) {
    position = 0;
  }
 
  window.requestAnimationFrame(myFunction);
};

window.requestAnimationFrame(myFunction);

So while requestAnimationFrame keeps calling our function over and over again, the value by which we move our div to the right keeps getting smaller and smaller hence creating a smooth, eased-out animation.

We have achieved an animated transition of our div from point A to point B by combining window.requestAnimationframe with linear interpolation and this duo has a wide range of use cases when it comes to web animations. I made a smooth scroll codepen to practice using this duo just to make sure I understood them more before putting this piece together.

I'm looking forward to exploring more use cases of lerp in my upcoming projects because I currently, I've come across like three or four and yes, that brings us to another subheading, CSS Transitions.

CSS Transitions - Behind The Scenes?

While I was trying to think of more use cases for linear interpolation on the web and a thought came in - "Could css animations and transitions be using lerp under the hood?" Well, I have no solid idea how css transitions work in-depth but this possibility of my thought being true isn't out of the question.

I also realised that we could save ourselves the stress of doing all these functions above by making use of the css transition property and then we'll have our animation + easing in just a few lines of code.

Here's the css

//css .box {
  transition: transform 2s ease-out;
}

and here's the javascript

//javascript const moveBox = () => {
  const ourDiv = document.querySelector(".box");
  ourDiv.style.transform = `translateX(500px)`;
}

moveBox();

That was f*cking easy and straight to the point right?? Yeah. Here's a fiddle to see this live.

Although you may think "Why then do we need to stress ourselves with all the interpolation and requestAnimationFrame stuff when CSS can already handle these for us?", well I don't really have an answer to that question right now but I noticed the easing with lerp and requestAnimationFrame is smoother and feels a lot more liquid than that of css-transition and I also can imagine a number of scenarios where using just css-transition alone will not save you.

I also noticed that when using css transitions, you have to manually provide the duration of the animation and this determines the speed but with lerp and requestAnimationFrame, your monitor's frame rate and the percentage you provide to the lerp function determines the speed of the animation. So in cases where you would want to wait for an animation to be completed before performing an action, css-transitions may not be able to give you that super-power.

Perhaps css transitions and animations make use of more complex or simple concepts alongside lerping. I'm looking forward to learning more about this and will update this piece accordingly or write a new one on my discovery.

See you some other time, byeeeeee!

Hello, I'm
Collins Enebeli.

avatar

I'm a software engineer focused on solving problems using frontend technology. I am interested in user experience, accessibility, design engineering, gaming, web3, web animations, cloud engineering and golang.

It's Christmas and I'm surely not suppose to be writing stuff at 2AM but here I am lol.

So I stumbled upon an unpopular part of the web lately and it's been from one 'WTF' to another. After a few hours of exploring various articles, videos and codepens, trying to understand in-depth concepts of web animations, I'm currently sitting on my new, comfortable chair trying to think of the best words for these paragraphs but the recursive thought running through my head is simple - I need to get better real quick".

I'll be explaining a few jargons I managed to pick up so far but here's an disclaimer - This article just compiles a number of basic stuff I learnt or think is true about these concepts so there may be some incorrect assumptions here and there but overall, it's honest work I guess.

window.requestAnimationFrame - What Is This? What Does It Do?

If you're not a big fan of moving stuff around frequently on a web page, you probably may have seen this method somewhere before now but then it turned out to be one of those unnecessary things in javascript that you don't need to clog your head with. I had the same thought as well until recently.

Keeping things simple, you can think of window.requestAnimationFrame as a setInterval method but specially optimised for your computer/smartphone and when called, runs multiple times within one second. Now let me explain further...

Let's say you have a function that you wish to call repeatedly every second. That's easy, we'll just use a normal setInterval. The setInterval will call the function every second and this continues infinitely till the tab/browser window is closed.

In order to understand properly what requestAnimationFrame does, let's imagine we have a div on our page and we wish to move this div to the right by 500px; In that case, we'll simply write a function that adds transform: translateX(500px) to the style property of the div, and then call the function once. Easy right?

Yeah, but it could be better, let's animate the movement of our div from A to point B by gradually moving the div to the right until it gets to its final point. We can achieve this by updating the translateX by a few pixels at an interval until it gets to its final destination using setInterval.

//javascript const ourDiv = document.querySelector("div");
let position = 0;

const myFunction = () => {
  position += 1;
  ourDiv.style.transform = `translateX(${position}px)`;

  if (position >= 500) {
    position = 0;
  }
};

const interval = setInterval(() => {
  myFunction();
}, 100);

I should embed a codepen here but their iframe doesn't look great on my blog so here's a link if you wish to see this animation live

One thing you should take note of is the interval we used and also the value by which we increment the position each time our function runs. The smaller the value of the interval, the smoother our animation gets and the larger the value of our increment, the faster the animation goes.

In this case, we're updating the position of our div by 1px 10 times in one second (100ms interval) hence creating an animated visual of the div from point A to point B and I could say that this is basically how videos work. Think of a video as a collection of images/snapshots taken at intervals and then played in a sequence, just like flipbook animations.

Now if you checked the codepen I shared earlier and the flipbook video link above, you'll see that both of them appear to be jumpy and not-smooth, this is because of the rate at which the frames are updated. In videography, this phenomenon is known as frame rate or FPS (frames per seconds). This is why when buying a new iPhone or Camera, you check and see something like 'can shoot at 30FPS or supports HD video at 60FPS'. What this basically means is, when taking a video with the camera, it captures 60 frames/shots/pictures per second and the plays these frames in a sequence to produce a video.

For this reason, cameras with higher FPS produce better quality videos because no frames are skipped and it captures every moment hence producing a smooth, less jumpy video.

Oh we're talking about cameras now and not javascript? Nah, here comes the interesting part.

So, putting all these I just said together, we need to make our animated div less jumpy and we can achieve this by increasing the FPS. Our current frame rate from the snippet above is about 10FPS which simply means we are updating our div 10 times per second. If we reduce the interval to 50ms, we'll have a frame rate of 20FPS which will give us a smoother animation.

Knowing that higher frame rate gives us better animations, we could just decide to be clever and use an interval of say, 5ms. This will give us an FPS of 200 and then our animation will be smoother and faster (a little too fast actually), but for a number of reasons which I won't be covering in this article, it takes a toll on the performance or our web page. Imagine calling a function 200 times in one second, a little too much right? This is where window.requestAnimationFrame comes in!

The window.requestAnimationFrame() method works just like our setInterval function up there but the difference is, it calls our animation function with a frame rate that matches the device monitor/screen specifications. Most monitors have a refresh rate of 60Hz, some high quality ones have over 100Hz and some even up to 240Hz.

A monitor's refresh rate is simply the maximum of times in one second an object can get updated on screen. So what requestAnimationFrame does is, it updates our animation in a rate per second that matches the refresh rate of whatever monitor we're viewing the webpage on.

This gives us the smoothest animation possible on that monitor while maintaining maximum performance. Also note that calling window.requestAnimationFrame() executes the function you put in it only once so we'll have to create a loop in order to continuously update our animation by adding another requestAnimationFrame() callback inside our function.

//javascript const ourDiv = document.querySelector("div");
let position = 0;

const myFunction = () => {
  position += 1;
  ourDiv.style.transform = `translateX(${position}px)`;
  
  if (position >= 500) {
    position = 0;
  }
  
  window.requestAnimationFrame(myFunction);
};

window.requestAnimationFrame(myFunction);

There you have it, requestAnimationFrame being useful. Now, in our code up here, the speed of our animation now depends on how many pixels you add to the position each time our function runs. Currently, our div moves from position A - B in a linear manner and this is totally fine but then we can decide to add some form of easing/smooth effects in our animation. This is where we get to make use of linear interpolation.

WTF is Linear Interpolation?

A linear interpolation function often called LERP is a function that takes in three values - start, end and percentage, and then returns a point between the start and end values based on the percentage. That's a little confusing but chile, I'll explain, here's an example.

//javascript const lerp = (start, end, percentage) => {
  // get the difference between start & end values
  const differenceBetween = end - start;

  // get a percentage of that value
  const lerpedValue = differenceBetween * percentage;

  // return the value of the calculated percentage
  return lerpedValue
};

// calling lerp with start = 0, end = 100 and percentage = 50%
lerp(0, 100, 0.5);

The function above takes in a starting point - 0 and final point - 100 and then returns a point that is 50% in between 0 and 100 which is obviously 50.

Okay, what's that gotta do with me? Chile, I'll explain.

Instead of increasing the position of our div by a constant 1px each time our function gets called to produce a linear animation, using a lerp function to interpolate between the current div position and the final position will produce a very smooth easing visual that slows our div down as it draws closer to its final destination.

Take for example, instead of using position += 1 in our animation function above (the one we used requestAnimationFrame) we instead decide to use position += lerp(position, 500, 0.1), the value of position will be equal to 50 when our function runs for the first time, then the next time our function runs the value of position will be smaller than 50 because the distance between starting point (0) and ending point (500) has changed by 10%.

Our new starting point is now 50 while our ending point remains the same (500), so the next time our function runs, the number of pixels by which we'll be moving our div will be 10% of [500 - 50] which is 45 (10% of 450).

//javascript const ourDiv = document.querySelector("div");
let position = 0;

const myFunction = () => {
  position += lerp(position, 500, 0.1);
  ourDiv.style.transform = `translateX(${position}px)`;
  
  if (position >= 500) {
    position = 0;
  }
 
  window.requestAnimationFrame(myFunction);
};

window.requestAnimationFrame(myFunction);

So while requestAnimationFrame keeps calling our function over and over again, the value by which we move our div to the right keeps getting smaller and smaller hence creating a smooth, eased-out animation.

We have achieved an animated transition of our div from point A to point B by combining window.requestAnimationframe with linear interpolation and this duo has a wide range of use cases when it comes to web animations. I made a smooth scroll codepen to practice using this duo just to make sure I understood them more before putting this piece together.

I'm looking forward to exploring more use cases of lerp in my upcoming projects because I currently, I've come across like three or four and yes, that brings us to another subheading, CSS Transitions.

CSS Transitions - Behind The Scenes?

While I was trying to think of more use cases for linear interpolation on the web and a thought came in - "Could css animations and transitions be using lerp under the hood?" Well, I have no solid idea how css transitions work in-depth but this possibility of my thought being true isn't out of the question.

I also realised that we could save ourselves the stress of doing all these functions above by making use of the css transition property and then we'll have our animation + easing in just a few lines of code.

Here's the css

//css .box {
  transition: transform 2s ease-out;
}

and here's the javascript

//javascript const moveBox = () => {
  const ourDiv = document.querySelector(".box");
  ourDiv.style.transform = `translateX(500px)`;
}

moveBox();

That was f*cking easy and straight to the point right?? Yeah. Here's a fiddle to see this live.

Although you may think "Why then do we need to stress ourselves with all the interpolation and requestAnimationFrame stuff when CSS can already handle these for us?", well I don't really have an answer to that question right now but I noticed the easing with lerp and requestAnimationFrame is smoother and feels a lot more liquid than that of css-transition and I also can imagine a number of scenarios where using just css-transition alone will not save you.

I also noticed that when using css transitions, you have to manually provide the duration of the animation and this determines the speed but with lerp and requestAnimationFrame, your monitor's frame rate and the percentage you provide to the lerp function determines the speed of the animation. So in cases where you would want to wait for an animation to be completed before performing an action, css-transitions may not be able to give you that super-power.

Perhaps css transitions and animations make use of more complex or simple concepts alongside lerping. I'm looking forward to learning more about this and will update this piece accordingly or write a new one on my discovery.

See you some other time, byeeeeee!