Skip to content

Instantly share code, notes, and snippets.

@idettman
Last active October 3, 2025 15:53
Show Gist options
  • Select an option

  • Save idettman/99584f43d0c5b3f72fc1e3db2c3b8fa6 to your computer and use it in GitHub Desktop.

Select an option

Save idettman/99584f43d0c5b3f72fc1e3db2c3b8fa6 to your computer and use it in GitHub Desktop.
HTML Animation Performance Test
const boxes = [];
const button = document.getElementById("toggle-button");
const boxContainer = document.getElementById("box-container");
const animationType = document.getElementById("type");
// create boxes
for (let i = 0; i < 1000; i++) {
const div = document.createElement("div");
div.classList.add("css-animation");
div.classList.add("box");
boxContainer.appendChild(div);
boxes.push(div.style);
}
let toggleStatus = true;
let rafId;
button.addEventListener("click", () => {
if (toggleStatus) {
animationType.textContent = " requestAnimationFrame";
for (const child of boxContainer.children) {
child.classList.remove("css-animation");
}
rafId = window.requestAnimationFrame(animate);
} else {
window.cancelAnimationFrame(rafId);
animationType.textContent = " CSS animation";
for (const child of boxContainer.children) {
child.classList.add("css-animation");
}
}
toggleStatus = !toggleStatus;
});
const duration = 6000;
const translateX = 500;
const rotate = 360;
const scale = 1.4 - 0.6;
let start;
function animate(time) {
if (!start) {
start = time;
rafId = window.requestAnimationFrame(animate);
return;
}
const progress = (time - start) / duration;
if (progress < 2) {
let x = progress * translateX;
let transform;
if (progress >= 1) {
x = (2 - progress) * translateX;
transform = `translateX(${x}px) rotate(${
(2 - progress) * rotate
}deg) scale(${0.6 + (2 - progress) * scale})`;
} else {
transform = `translateX(${x}px) rotate(${progress * rotate}deg) scale(${
0.6 + progress * scale
})`;
}
for (const box of boxes) {
box.transform = transform;
}
} else {
start = null;
}
rafId = window.requestAnimationFrame(animate);
}
/*
The animation can be switched to requestAnimationFrame() by clicking the toggle button.
Try running them both now, comparing the FPS for each (the first purple box.) You should see that the performance of CSS animations and requestAnimationFrame() are very close.
Off main thread animation
Even given the test results above, we'd argue that CSS animations are the better choice.
But how?
The key is that as long as the properties we want to animate do not trigger reflow/repaint (read CSS triggers for more information),
we can move those sampling operations out of the main thread.
Off the main thread compositing: https://wiki.mozilla.org/Platform/GFX/OffMainThreadCompositing
The most common property is the CSS transform.
If an element is promoted as a layer, animating transform properties can be done in the GPU.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment