Summary - use CCapture.js to save PNGs of p5.js animations and ScreenToGif to convert those PNGs to a GIF.

Introduction

Processing and p5.js are powerful sketching tools. They generate images, animations, and interactive content. Processing code is written in Java and runs as a standalone program on your machine. p5.js is a JavaScript library that generates an HTML canvas in the browser. Both tools can generate animations that can be viewed in real-time, but both require a bit more work to generate a GIF or video file to share. I struggled through the process of generating a GIF with p5.js. Here’s what I’ve learned.

There are two basic things that need to be done:

  1. Save animation frames as images
  2. Convert those images to a video or GIF

There are already some nice tutorials out there on generating GIFs from Processing. Here is one. But one problem with p5.js compared to Processing is that is that it’s not as easy to save PNG files from the animation. The saveFrame() command in p5.js will only let you download 15 frames. Something different is needed for anything larger than that. Many tutorials also recommend command line GIF encoding - I wanted to find a way around that.

Here is my process:

  1. Save p5.js animation frames as PNGs using CCapture.js
  2. Use ScreenToGif to generate a GIF from the images

Step 1 - Save Animation Frames with CCapture.js

CCapture.js is a tool for generating videos (GIF or WebM) or images (png or jpeg) from an HMTL canvas (any canvas, not just a p5.js canvas). I tried the GIF generation tool that comes built in to CCapture first, but it frequently glitches and drop objects, so I would not recommend it. The WebM export option does work well, if you want to go that route. But I wanted to make GIFs, so instead of saving directly to a GIF, I save the animation frames as individual PNG files. CCapture bundles them into a single TAR file for download at the end of the animation. TAR files are like ZIP files. 7-Zip is a nice program for unpacking them.

To use CCapture.js, download and save the min file to a JS directory and reference it in the HTML sketch file. In my sketches, I also include a button that allows me to first preview the animation and then click the button to save the frames once I’m ready. You can see it in action below. Notice how the animation starts over and slows down as soon as the button is pressed. The button resets the frameCount to 1 and the animation slows down because of the added processing needed by the browser to save the frames.

I’ve created a template that includes the html and js sketch file and then p5.js and CCapture.js min files. It can be found here on GitHub. It’s everything you need to create a p5.js sketch, run an animation in the browser, and save it as PNG files from your browser. I’ve also included the HTML and JS below at the bottom of this page if you would prefer to copy/paste (just make sure you have CCapture.all.min.js and p5.min.js in your JS directory).

Step 2 - Make a GIF from PNG Files

There are a few common tools for doing this:

  1. ImageMagick - command line
  2. FFmpeg- command line
  3. ezGIF - online
  4. ScreenToGif - free program - recommended

Command line tools

Most tutorials I’ve seen recommend using the command line with ImageMagick orFFmpeg to convert PNGs to a GIF. It might look something like this for ImageMagick:

> magick convert -delay 2 -monitor -dither None *.png -loop 0 output.gif

or this with FFmpeg:

> ffmpeg -i %07d.png -vf fps=50,scale=750:-1 output.gif

There are a ton of filter and other options available with both of these, which I’m not familiar enough with to go into. But I want to avoid using the command line, so I looked for something else.

Online tools

To get away from the command line, I started using ezGIF.com. You can upload the PNG files, sort and change the delay for each frame, delete unwanted frames, reverse the animation, optimize the GIF, etc. It’s a nice interface with a lot of good options, and much easier to use than the command line. Unfortunately, the site would run really slow for me at times, or hang up, so I went searching for other options.

Programs

ScreenToGif is the best tool I’ve found for creating GIFs. It’s a free program with a great user interface. It’s advertised as a screen recording program, and it can do that (I used it to capture the GIF above), but it can also import image files and create videos and GIFs from them. Like ezGIF, things are easily customizable and intuitive. It’s straightforward to change the delay of one or all of the frames, delete frames, rearrange frames, and resize the images. There are also numerous different optimization levels and options available to reduce the file size. It’s made my workflow so much easier. Here’s what I do:

First, push ‘load’ to import images:

Navigate to the PNG files that you have already extracted from the TAR file (generated by CCapture.js) and select them all (using ctrl-a to speed things up):

Next you can do things like change the frame rate (20ms would make a GIF at 50fps). Other options would be resizing the images, reversing the frames, reducing the frame count, adding texts or progress bars, etc.

Once you’re ready to save the GIF, navigate back to File -> Save As and make your choices. Try a few optimizations or encoders to see what gives you the best results (note, there is an option to use FFmpeg or gifski to encode the gifs - both typically implemented with the command line, but ScreenToGif makes them much easier to use). I’ve been happy with the ScreenToGif encoder so far.

Here is the result:

Tips for Making GIFS

  • Make a loop (nice tutorial here)
  • Keep the image size moderate (somewhere around 750x750 tends to work well)
  • Try to keep the frame count below 300 or so. Less is better. (I’ve done more, but you may have to start sacrificing on GIF optimization)
  • Monochromatic images can help reduce GIF size
  • If you want to have a ‘pause’ in the animation, just change one of the frame delays manually in ezGIF or ScreenToGif (the gif at the top of this page has a 1000 ms delay set on the image in the middle of then GIF when all of the ‘frames’ are displayed).
  • If you want to make a boomerang, or reversing loop, you can do it in post-processing instead of in the animation - ScreenToGif makes it easy. That’s how I did this animation.

Summary

I use CCpature.js to save PNG files from the p5.js canvas and then ScreenToGif to convert the image files to GIF videos so that they can be uploaded and shared online. This is the easiest and most user friendly workflow I’ve found. I especially like the ScreenToGif interface, ability to edit individual frames and rearrange things with a click, and avoiding the command line.

HTML and JS Template

Here is my template for creating PNGs from p5.js animations using CCapture.js. These files can also be downloaded from GitHub.

<html lang="en">
  <head></head>
  <body> 
    <script src="js/p5.min.js"></script>
    <script src="js/CCapture.all.min.js"></script>
    <script src="js/sketch.js"></script>
    <input onclick="buttonPress()" type="button" value="Save Frames" id="myButton"></input>  
  </body>
</html>
// template for saving png files from p5.js sketch using CCapture
var capture = false; // default is to not capture frames, can be changed with button in browser
var capturer = new CCapture({format:'png'});
const NUM_FRAMES = 100;
const T = 1;

function setup() {
    createCanvas(750, 750);
}

function draw() {
    if (capture && frameCount==1) capturer.start(); // start the animation capture
    // here is the sketch
    background(0)
    var t = ((frameCount-1)%NUM_FRAMES)/NUM_FRAMES
    var x = width/3*sin(TWO_PI*t);
    var y = width/3*cos(TWO_PI*t);
    fill(255);
    noStroke();
    translate(width/2,height/2)
    ellipse(x,y,width/10)
    if (capture){
        capturer.capture( canvas ); // if capture is 'true', save the frame
        if (frameCount-1 == NUM_FRAMES){ //stop and save after NUM_FRAMES
            capturer.stop(); 
            capturer.save(); 
            noLoop(); 
        }
    }
}

function buttonPress()
{
    if (capture == false) {
        capture = true;
        document.getElementById("myButton").value='Saving Frames... Press Again to Cancel'; 
        frameCount = 0;
    } else {
        location.reload(); //refresh the page (starts animation over, stops saving frames)
    }
}