DEV Community

Cover image for 6 Proven Techniques for Creating Smooth 60fps Web Animations
Aarav Joshi
Aarav Joshi

Posted on

6 Proven Techniques for Creating Smooth 60fps Web Animations

As a best-selling author, I invite you to explore my books on Amazon. Don't forget to follow me on Medium and show your support. Thank you! Your support means the world!

Modern web interfaces demand fluid animations that respond instantly to user interactions while maintaining performance across all devices. As a frontend developer with years of experience optimizing animation performance, I've learned that the difference between a sluggish interface and a delightful one often comes down to technical implementation details. Let me share six proven techniques that have transformed my animation work.

Understanding the Browser Rendering Pipeline

Before diving into specific techniques, it's essential to understand how browsers render content. The rendering pipeline typically follows these steps: JavaScript → Style calculations → Layout → Paint → Composite. Each step takes processing time, and the goal of performance optimization is to minimize the work in each phase.

I've found that many performance issues stem from forcing the browser to recalculate layout (also called reflow) or repaint large portions of the screen. By focusing on techniques that avoid these expensive operations, we can create significantly smoother animations.

GPU-Accelerated Properties

The most impactful change I've implemented in my animation work is focusing on GPU-accelerated properties. Properties like transform and opacity can be processed directly by the graphics hardware, bypassing much of the rendering pipeline.

/* Inefficient approach that triggers layout */
.element {
  animation: move 1s infinite;
}
@keyframes move {
  0% { left: 0; top: 0; }
  100% { left: 100px; top: 100px; }
}

/* Optimized approach using transform */
.element {
  animation: move 1s infinite;
}
@keyframes move {
  0% { transform: translate(0, 0); }
  100% { transform: translate(100px, 100px); }
}
Enter fullscreen mode Exit fullscreen mode

This simple change can dramatically improve performance, especially on mobile devices. I've seen animations go from choppy 15fps to smooth 60fps just by switching from animating position properties to using transforms.

Strategic Use of will-change

The will-change property allows us to hint to browsers about elements that will animate, enabling them to prepare optimizations ahead of time. This can be particularly effective for complex animations.

.menu-icon {
  will-change: transform;
}

.menu-icon:hover {
  transform: scale(1.2);
}
Enter fullscreen mode Exit fullscreen mode

However, I've learned to use this property judiciously. Early in my career, I applied it too liberally, which actually hurt performance by consuming excessive memory. Now I only use it on elements that will definitely animate and remove it when the animation completes if possible.

// Add will-change before animation starts
element.style.willChange = 'transform';

// Remove it after animation completes
element.addEventListener('transitionend', () => {
  element.style.willChange = 'auto';
});
Enter fullscreen mode Exit fullscreen mode

Leveraging requestAnimationFrame

When creating JavaScript animations, synchronizing with the browser's rendering cycle is crucial. The requestAnimationFrame API ensures our animation code runs at the optimal time in each frame.

const element = document.querySelector('.animated-element');
let position = 0;
let direction = 1;

function animate() {
  // Update position
  position += 2 * direction;

  // Reverse direction at boundaries
  if (position > 100 || position < 0) {
    direction *= -1;
  }

  // Apply the new position
  element.style.transform = `translateX(${position}px)`;

  // Request next frame
  requestAnimationFrame(animate);
}

// Start the animation
requestAnimationFrame(animate);
Enter fullscreen mode Exit fullscreen mode

This approach has consistently delivered smoother animations than using setTimeout or setInterval, as it respects the browser's natural rendering rhythm and pauses when the tab is inactive, conserving battery life.

Throttling Scroll-Based Animations

Scroll events fire at a high frequency, and attempting to run complex animations on each event can bring even powerful devices to a crawl. I've developed the habit of throttling these animations to maintain a reasonable frame rate.

let lastScrollPosition = window.scrollY;
let ticking = false;

function updateParallax() {
  // Calculate how far we've scrolled
  const scrolled = window.scrollY;

  // Update parallax elements
  document.querySelectorAll('.parallax-element').forEach(element => {
    const speed = element.dataset.speed || 0.5;
    element.style.transform = `translateY(${scrolled * speed}px)`;
  });

  ticking = false;
  lastScrollPosition = scrolled;
}

window.addEventListener('scroll', () => {
  if (!ticking) {
    requestAnimationFrame(updateParallax);
    ticking = true;
  }
});
Enter fullscreen mode Exit fullscreen mode

This technique has been particularly valuable for parallax effects and scroll-triggered animations, preventing the jerky movement that often plagues these interfaces.

Pausing Off-Screen Animations

One of the most overlooked performance improvements is simply pausing animations that aren't currently visible. The Intersection Observer API makes this quite straightforward.

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    const animationElement = entry.target;

    if (entry.isIntersecting) {
      // Element is visible, start/resume animation
      animationElement.style.animationPlayState = 'running';
    } else {
      // Element is off-screen, pause animation
      animationElement.style.animationPlayState = 'paused';
    }
  });
}, { threshold: 0.1 }); // Trigger when at least 10% is visible

// Observe all animated elements
document.querySelectorAll('.animated-element').forEach(element => {
  observer.observe(element);
});
Enter fullscreen mode Exit fullscreen mode

I've implemented this technique on content-heavy pages with dozens of animations, reducing CPU usage by up to 80% and significantly extending battery life on mobile devices.

Composite-Only Animations

For maximum performance, I aim to create animations that only affect the composite phase of rendering. By avoiding layout and paint operations, the browser can process these animations extremely efficiently.

/* This animation requires only compositing */
.efficient-animation {
  transform: translateZ(0); /* Promotes to its own layer */
  transition: transform 0.3s ease, opacity 0.3s ease;
}

.efficient-animation:hover {
  transform: scale(1.1) translateZ(0);
  opacity: 0.9;
}
Enter fullscreen mode Exit fullscreen mode

Using Chrome DevTools' Performance panel has been invaluable in identifying when animations are causing layout or paint operations. When I see these issues, I refactor the animation to use composite-only properties.

Practical Application: Menu Animation Example

Let me demonstrate how these techniques come together in a real-world example. Here's a hamburger menu animation that transforms into an X icon:

<button class="menu-button" aria-label="Menu">
  <div class="bar bar1"></div>
  <div class="bar bar2"></div>
  <div class="bar bar3"></div>
</button>
Enter fullscreen mode Exit fullscreen mode
.menu-button {
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 10px;
  width: 40px;
  height: 40px;
  position: relative;
}

.bar {
  background: #333;
  height: 4px;
  width: 100%;
  position: absolute;
  left: 0;
  transform-origin: center;
  transition: transform 0.3s ease, opacity 0.3s ease;
  will-change: transform, opacity;
}

.bar1 { top: 8px; }
.bar2 { top: 18px; }
.bar3 { top: 28px; }

/* Open state */
.menu-open .bar1 {
  transform: translateY(10px) rotate(45deg);
}

.menu-open .bar2 {
  opacity: 0;
}

.menu-open .bar3 {
  transform: translateY(-10px) rotate(-45deg);
}
Enter fullscreen mode Exit fullscreen mode
const menuButton = document.querySelector('.menu-button');

menuButton.addEventListener('click', () => {
  menuButton.classList.toggle('menu-open');
});
Enter fullscreen mode Exit fullscreen mode

This animation uses GPU-accelerated properties, applies will-change judiciously, and avoids layout thrashing, resulting in a smooth 60fps animation even on mid-range mobile devices.

Measuring Animation Performance

To validate these techniques, I regularly use performance measurement tools. Chrome DevTools' Performance panel is my primary resource, but I also rely on the Frame Timing API for real-user monitoring:

function trackFrameRate() {
  if (!window.performance || !window.performance.now) {
    console.log('Performance API not supported');
    return;
  }

  let lastTime = performance.now();
  let frames = 0;
  let frameTimes = [];

  function countFrame(now) {
    // Calculate time since last frame
    const delta = now - lastTime;
    frameTimes.push(delta);

    // Keep only the last 60 frames
    if (frameTimes.length > 60) {
      frameTimes.shift();
    }

    frames++;
    lastTime = now;

    // Log statistics every second
    if (frames % 60 === 0) {
      const averageTime = frameTimes.reduce((sum, time) => sum + time, 0) / frameTimes.length;
      const fps = 1000 / averageTime;
      console.log(`Average FPS: ${fps.toFixed(1)}`);
    }

    requestAnimationFrame(countFrame);
  }

  requestAnimationFrame(countFrame);
}

// Start tracking when animations begin
document.querySelector('.start-animation-button').addEventListener('click', () => {
  trackFrameRate();
  // Start animation logic here
});
Enter fullscreen mode Exit fullscreen mode

This code has helped me identify performance bottlenecks and validate improvements across different devices and browsers.

Optimizing for Different Devices

One of the most challenging aspects of web animation is accounting for the wide variety of devices accessing your content. I've found that implementing progressive enhancement for animations works extremely well:

// Check if the device likely has limited processing power
const isLowPowerDevice = () => {
  return (
    /Android/.test(navigator.userAgent) && /Mobile/.test(navigator.userAgent) ||
    /iPhone|iPad|iPod/.test(navigator.userAgent) ||
    (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches)
  );
};

// Apply different animation settings based on device capability
document.querySelectorAll('.animated-element').forEach(element => {
  if (isLowPowerDevice()) {
    // Simplified animation for low-power devices
    element.style.transition = 'transform 0.5s ease-out';
  } else {
    // Rich animation for powerful devices
    element.style.transition = 'transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)';
    // Add additional animations for high-end devices
    element.addEventListener('mouseover', applyHoverEffects);
  }
});
Enter fullscreen mode Exit fullscreen mode

This approach ensures a good experience across the board while providing enhanced animations on capable devices.

Conclusion

Creating smooth web animations is both an art and a science. By focusing on GPU-accelerated properties, using will-change strategically, leveraging requestAnimationFrame, throttling scroll animations, pausing off-screen animations, and building composite-only animations, I've been able to craft experiences that delight users without draining their batteries.

The techniques I've shared come from years of trial and error, investigating frame rate drops, and refining approaches. While the specific implementations might evolve with browser technologies, the underlying principles of minimizing layout recalculations, reducing paint operations, and optimizing for the GPU will remain relevant for years to come.

Remember that performance is a feature that users feel rather than see explicitly. The smoother your animations, the more professional and polished your application will appear. By implementing these six techniques, you'll be well on your way to creating web animations that feel native, responsive, and effortless.


101 Books

101 Books is an AI-driven publishing company co-founded by author Aarav Joshi. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as $4—making quality knowledge accessible to everyone.

Check out our book Golang Clean Code available on Amazon.

Stay tuned for updates and exciting news. When shopping for books, search for Aarav Joshi to find more of our titles. Use the provided link to enjoy special discounts!

Our Creations

Be sure to check out our creations:

Investor Central | Investor Central Spanish | Investor Central German | Smart Living | Epochs & Echoes | Puzzling Mysteries | Hindutva | Elite Dev | JS Schools


We are on Medium

Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Science & Epochs Medium | Modern Hindutva

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)