Skip to main content

Simple "Pause GIFs" Button Tutorial

Hey there! Welcome to my tutorial on how to make a simple "Play/Pause GIFs" button. This is a simplified version of the button I use on my site. (Right now it's only on my "About Me" page, but that'll change soon.) I will probably make a tutorial on the full version later, but that'll be a bit more complicated.

Having a button like this is a big win for accessibility. Some people are affected by motion on the screen. It can make them nauseous, cause seizures or migraines, or just be distracting. (Personally, if I'm trying to read a longer article or info page, having a bunch of moving stuff can make it really hard for me to focus.)

I will note that my approach can be a bit labor-intensive. You will have to make a static (non-moving) version of every animated GIF on your site. If that's too much, you can choose to make only some of your GIFs pausable. You can easily do just a few at a time, probably starting with the biggest ones.

There are a few other ways to do this, which may work better for your site than mine (many thanks to their creators!!!):

  • Some people use a JavaScript library called Freezeframe.js. It can apparently be tricky to use, though, and has some other issues.
  • Bechno Kid's script is a simplified version of Freezeframe. The nice thing about it is you don't have to make static versions of your GIFs! You just drop in the script and make a few HTML changes. If you have a LOT of animated images, that may be the way to go.
  • Solaria's tutorial uses a similar technique to mine. It finds images to pause by file extension, so you don't need a longer HTML block for every image. You'll still have to make static PNGs of every GIF, though.
So... why do I use this particular method? (click to expand)
  • It's simple. Without any comments or white space, it would only be 31 lines of plain JavaScript. No external libraries, no weird HTML5 hacks, or anything else that could cause headaches. It would be pretty hard for me to accidentally break something, which I do often lol
  • I can choose which frame to pause a GIF on. Most other methods I've seen (notably Freezeframe) pause GIFs on their first animation frame, which often isn't the best one.
  • It follows the "reduce motion/animation" settings that people can set on their phones or computers.
  • I can make people's preferences follow them from page to page without storing anything in their web browser. That's a more advanced feature that's out of scope for this guide, but I'll probably make a tutorial on it later. It'll build off of this one.

The Code

I'll drop the code here first, then explain how to use it. Feel free to use, change, and repost my code however you like! You don't have to link back here, but you might want to if you think you'll reference this tutorial again later. You'll need both the JavaScript and HTML code below.
Note: If you're viewing this on a smaller screen, you might have to scroll horizontally to see it all.

JavaScript code (click to expand)
(function () {

    // Find and un-hide Play/Pause GIFs button
    const toggleButton = document.getElementById("pause-gifs-button");
    if (toggleButton) {
        toggleButton.hidden = false;
    }

    // Detect prefers-reduced-motion (OS "reduce motion" setting)
    const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    // Determine initial state from reduced-motion preference
    let gifsPlaying = !prefersReducedMotion;

    // Create a list of animated images on the page
    const animatedImages = document.querySelectorAll("img.animated");

    // Early exit if button or images are missing
    if (!toggleButton || animatedImages.length === 0) {
        return;
    }

    // Function to update button text and aria-pressed state
    function updateButtonText() {
        toggleButton.textContent = gifsPlaying ? "Pause GIFs" : "Play GIFs";
        toggleButton.setAttribute("aria-pressed", gifsPlaying ? "true" : "false");
    }

    // Function to apply the current state to all images
    function updateImages() {
        animatedImages.forEach(img => {
            const targetSrc = gifsPlaying ? img.dataset.animated : img.dataset.static;
            if (targetSrc) {
                img.src = targetSrc;
            }
        });
    }

    // Handle button click (mouse or keyboard activation)
    toggleButton.addEventListener("click", function () {

        gifsPlaying = !gifsPlaying;

        updateImages();
        updateButtonText();
    });

    // Initial setup on page load
    updateImages();
    updateButtonText();

})();
First HTML snippet (click to expand)
<button id="pause-gifs-button" hidden>
    Play GIFs
</button>
Second HTML snippet (click to expand)
<img class="animated"
    src="/assets/images/example-gif_static.png"
    data-static="/assets/images/example-gif_static.png"
    data-animated="/assets/images/example-gif_animated.gif"
    alt="An example GIF for demonstration purposes">

How to Use It

Firstly, you'll need to put the JavaScript code somewhere on your page. I like to keep my scripts in separate .js files, but dropping it in-line works too. It's best to put the <script> tags at the end of your page, right before your closing </body> tag. You should be able to use the JavaScript as-is; no need to change anything unless you want to.

You will also need to put the button somewhere on your page using the first HTML snippet. It's probably best to put it near the top of your page. That will make sure it's easy to find and easy to tab to with a keyboard.

The second HTML snippet is for your images. As I said in the intro, you will need to make a static (non-animated) version of every animated image that you want to be pause-able. Then, you need to use the snippet above instead of "normal" image tags. You will have to change four things for each image:

  • src should be the path to your image file. Either the static or animated version works here. Whichever one you use will be the one that people with JavaScript turned off will see.
  • data-static needs to be the path to the static (non-animated) version of your image file.
  • Similarly, data-animated needs to be the path to the animated GIF version.
  • alt should either be changed to descriptive alt text or left blank (alt="") for images that are purely decorative. "Purely decorative" images are things like custom borders and dividers that people using screen readers don't need to hear about.

That's kinda it. If you've read this far, you should be able to use the code with no problem. Feel free to ask questions if you need to. If you want to know how it works, read on!


How it Works

Currently working on this. Might take me a bit. Come back soon!