How to Pause an Animated GIF with CSS and JavaScript

This article provides a practical guide on how web developers can implement user-controlled pause and play functionality for animated GIFs using HTML5, CSS, and JavaScript. Because the standard GIF format lacks a native playback control API, developers cannot simply call a pause method on an image element. Instead, pausing must be simulated either by swapping the animated file with a static image or by freezing the current frame dynamically onto an HTML5 <canvas> element.

The Challenge with Native GIFs

Browsers treat animated GIFs as standard images rendered via the <img> tag. Unlike HTML5 <video> or WebM formats, standard image tags do not support methods like play() or pause(). To give users control—an essential feature for accessibility and preventing distractions—developers must capture and display a frozen state of the animation on demand.

Method 1: The Canvas Freeze Technique (Dynamic Pause)

The most seamless approach captures the exact frame displayed when the user triggers the pause action, rendering it onto a hidden <canvas> layered directly over the GIF.

1. HTML Structure

Wrap the image, a canvas element, and a control button in a container:

<div class="gif-container">
  <img id="animated-gif" src="animation.gif" alt="Animated illustration" crossOrigin="anonymous">
  <canvas id="gif-canvas"></canvas>
  <button id="toggle-btn" aria-label="Pause animation">Pause</button>
</div>

2. CSS Styling

Ensure the canvas directly overlaps the image, remaining hidden until the pause state is active:

.gif-container {
  position: relative;
  display: inline-block;
}

#gif-canvas {
  position: absolute;
  top: 0;
  left: 0;
  display: none;
  pointer-events: none;
}

.paused #gif-canvas {
  display: block;
}

3. JavaScript Implementation

Use JavaScript to draw the current frame to the canvas when paused, and clear it to resume playback:

const container = document.querySelector('.gif-container');
const img = document.getElementById('animated-gif');
const canvas = document.getElementById('gif-canvas');
const button = document.getElementById('toggle-btn');
const ctx = canvas.getContext('2d');

let isPaused = false;

button.addEventListener('click', () => {
  if (!isPaused) {
    // Set canvas dimensions to match the image
    canvas.width = img.clientWidth;
    canvas.height = img.clientHeight;

    // Draw the current frame of the GIF onto the canvas
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

    // Show canvas overlay
    container.classList.add('paused');
    button.textContent = 'Play';
    button.setAttribute('aria-label', 'Play animation');
    isPaused = true;
  } else {
    // Hide the canvas overlay to show the underlying animation
    container.classList.remove('paused');
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    button.textContent = 'Pause';
    button.setAttribute('aria-label', 'Pause animation');
    isPaused = false;
  }
});

Note: Due to how GIFs run in the browser rendering pipeline, resuming the GIF will not continue from the exact millisecond it stopped; rather, it will reveal the GIF at its current internal animation cycle.

Method 2: The Source Swap Technique (Static Image)

If capturing the exact frame is unnecessary, swapping between a static image (such as a JPEG or PNG of the first frame) and the animated GIF offers a lightweight alternative.

1. HTML with Data Attributes

Store both sources in custom data attributes:

<img 
  id="toggle-gif" 
  src="animation.gif" 
  data-animated="animation.gif" 
  data-static="static-frame.png" 
  alt="Demonstration graphic"
>
<button id="swap-btn">Pause</button>

2. JavaScript Toggle

Update the src attribute when the user toggles the button:

const gif = document.getElementById('toggle-gif');
const swapBtn = document.getElementById('swap-btn');

swapBtn.addEventListener('click', () => {
  const isPlaying = gif.src.endsWith(gif.dataset.animated);

  if (isPlaying) {
    gif.src = gif.dataset.static;
    swapBtn.textContent = 'Play';
  } else {
    gif.src = gif.dataset.animated;
    swapBtn.textContent = 'Pause';
  }
});

Supporting User Accessibility Preferences

To respect users who have configured their operating systems to minimize non-essential motion, incorporate the CSS prefers-reduced-motion media query. Developers can combine this media query with JavaScript to default GIFs to their paused or static state automatically:

const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

if (prefersReducedMotion) {
  // Automatically activate the paused state on initial load
  button.click();
}