In this post, we will see how to create animations using only JavaScript, which can become a great resource when performing our developments; creating animations with JavaScript is simpler than it seems and to make it more interesting, this API is integrated with the Canvas API which increases its versatility and simplicity of use.
Creating animations in JavaScript may seem complex at first, but in practice, it is much simpler than many imagine. In my case, for a long time, I thought that to achieve fluid animations it was mandatory to resort to external libraries or CSS, until I started working directly with requestAnimationFrame() and, above all, combining it with Canvas.
In this post, we will see how to create animations using only JavaScript, why requestAnimationFrame() is the correct way to do it, and how to take advantage of it in practical examples that actually work well in the browser.
Remembering “classic” animations in JavaScript
If you have been developing in JavaScript for some time, setTimeout() and setInterval() surely sound familiar. For years they were the standard solution when we wanted to execute code periodically or simulate an animation.
A typical example was something like this:
function draw() { setTimeout(paint, 100); // drawing code } paint();The problem is that these functions are not intended for animations. On more than one occasion I encountered frozen browsers, non-fluid animations, or exaggerated CPU consumption simply by executing drawing logic without real control over the screen repainting.
The biggest drawback is that:
- They are not synchronized with the browser's refresh rate.
- They keep running even when the tab is in the background.
- They do not allow for automatic optimization by the browser.
Animating with libraries: convenient, but not always necessary
Another common option is to use animation libraries or plugins. And there is nothing wrong with that: many automate much of the work and offer excellent results.
However, over time I discovered that understanding the native API gives you much greater control. For custom animations, simple physics, Canvas, or state-dependent logic, knowing requestAnimationFrame() makes a huge difference.
Animating a Canvas with Window.requestAnimationFrame()
The function we mentioned at the beginning that allows animating objects in the Canvas is called Window.requestAnimationFrame() which takes as a parameter the function in charge of painting (and repainting) -in our case- the Canvas; for example, following the same scheme used in "classic" animations we have:
function draw() {
requestAnimationFrame(draw);
// Drawing code goes here
}
draw();This API tells the browser that:
- We want to perform an animation.
- It must execute a function just before the next repaint.
- Synchronize execution with the refresh frequency (usually 60 FPS).
With the
requestAnimationFrame()function, smooth transitions or changes are obtained through an API that is optimized for such purposes.
Something important happens here: requestAnimationFrame() is not an automatic loop. Each call schedules a single frame, so we must call it again within the function itself to keep the animation active.
When I tried it for the first time, what surprised me most was the immediate smoothness compared to setInterval(), even with simple code.
Why is requestAnimationFrame better than setInterval?
The advantages are clear:
- ✔️ Smoother animations.
- ✔️ Lower CPU and battery consumption.
- ✔️ Automatically pauses in inactive tabs.
- ✔️ The browser decides the best time to draw.
Instead of forcing a fixed interval, we let the browser optimize the repainting and group all animations into a single cycle.
Animating a square in Canvas with requestAnimationFrame()
One of the most powerful combinations is Canvas + requestAnimationFrame(). This is where the versatility of this API is really noticed.
In this small experiment, we will see how to animate a simple square; specifically, we will make the square move from one diagonal to the other; the following JavaScript code does exactly that:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// canvas dimensions
var width = canvas.width = window.innerWidth;
var height = canvas.height = window.innerHeight;
// initial positions
var x = width;
var y = 0;
// velocity
var vx = 3;
var vy = 2;
function animate() {
// we clear the canvas
ctx.clearRect(0, 0, width, height);
// we draw a 10x10 square
ctx.fillRect(x, y, 10, 10);
// we update the position
x -= vx;
y += vy;
// we request the next frame
requestAnimationFrame(animate);
// we validate boundaries
if (y > height || x < 0) {
x = width;
y = 0;
}
}
animate();Let's analyze the code presented above:
- First, we initialize the basic elements to be able to work with Canvas.
var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var width = canvas.width = window.innerWidth; var height = canvas.height = window.innerHeight;- We define the default position (or X and Y coordinates) where the figure is initially drawn; furthermore, a couple of variables are defined to specify the speed of the animation. // positions var x = width; var y = 0; // speed var vx = 3; var vy = 2;
- One of the tasks performed by the
animate()function is to update thexandyvariables in order to vary the position of the square. // we update the position x -= vx; y += vy;
This section is fundamental because it is where we alter the position of the square, which is necessary to perform the animation.
- We invoke the method
requestAnimationFrame(animate);recursively specifying the name of the function (animate()) as a parameter.
Finally, the animation with JavaScript:
By simply removing the call to the function ctx.clearRect() and thus not clearing the Canvas, we obtain the following result:
Refresh to see the animation.
And if we alter the "speed" values to change them between positive and negative and validate that it doesn't go out of range:
if(vx > 0){
if (x > width)
vx *=-1;
if (y > height) {
vx = -8;
vy = 2;
x=width;
y=0;
}
}else
if (x < 0)
vx *=-1;
}We get:
What happens if we don't clear the Canvas?
An interesting detail is that, if we delete this line:
ctx.clearRect(0, 0, width, height);The canvas is not cleared, and the square leaves a visual trail. With just that small change we get a completely different effect, something I discovered by testing and experimenting with the code.
This type of testing helps a lot to understand how rendering actually works.
Value returned by Window.requestAnimationFrame()
When invoking the requestAnimationFrame() function, it returns a non-zero code that must be used in case you want to cancel the animation:
id = requestAnimationFrame(callback);
cancelAnimationFrame(id);Changing direction and speed
If we alter the speed values and validate the boundaries, we can make the object bounce or change direction:
if (vx > 0) {
if (x > width) vx *= -1;
if (y > height) {
vx = -8;
vy = 2;
x = width;
y = 0;
}
} else {
if (x < 0) vx *= -1;
}This is where requestAnimationFrame() demonstrates its utility for dynamic animations, dependent on state and not just time.
Use of timestamp: hardware-independent animations
An important point that is often overlooked is the use of the timestamp received by the callback:
function animate(timestamp) {
// use timestamp to calculate displacements
}Using this value (or performance.now()) prevents the animation from running faster on screens with high refresh rates (120 Hz, 144 Hz). It is a good practice, especially in more complex or physical animations.
Canceling an animation with cancelAnimationFrame()
requestAnimationFrame() returns an identifier that we can use to stop the animation:
let id = requestAnimationFrame(animate);
cancelAnimationFrame(id);This is very useful when:
- We want to pause the animation.
- The user changes the view.
- The animation has already fulfilled its objective.
When to use requestAnimationFrame and when not to?
In general:
- ✅ Canvas, physical animations, complex logic → requestAnimationFrame
- ✅ Simple UI animations → CSS
- ❌ Avoid setInterval for animations
Whenever the animation depends on data, states, or calculations, requestAnimationFrame() ends up being the best option.
FAQs about requestAnimationFrame()
- Does requestAnimationFrame work at 60 FPS?
- Normally yes, but it adapts to the device's refresh rate.
- Does it pause in the background?
- Yes, the browser reduces or pauses execution in inactive tabs.
- Can I use it without Canvas?
- Of course. It works with both DOM and Canvas.
- Is it better than setInterval?
- For animations, clearly yes.
Conclusion
requestAnimationFrame() is an essential API for creating fluid, efficient, and modern animations in JavaScript. Understanding how it works, especially alongside Canvas, opens up many possibilities without the need for external libraries.
Once you internalize that animation consists of updating state + drawing + repeating, everything fits naturally and the code becomes much more predictable and maintainable.
I agree to receive announcements of interest about this Blog.
We will see how to make some simple animations in JavaScript using requestAnimationFrame() whose API is integrated with Canvas.