How to save your p5.js sketch as a GIF using CCapture.js

Hello everyone,

I recently figured out how to save my canvas as a GIF using Ccapture.js - something I’ve been trying to do since I started using p5.js a few months ago - so I made a video on how to get it working with a simple sketch:

I got the code from here, text is in Japanese but code is pretty self explanatory - so if you’re in a hurry and more experienced, check it out.

It took me forever to find the above link so I thought I’d show other beginners like me looking to share their sketches as GIFs how to get it working and post it on the official forum for better visibility.

I’m sure I got something wrong in the video, but it works.
Please feel free to correct me or add anything else in the comments for I am but a n00b.

P.S. If you’re watching the video from in here, here a note I left in the YouTube comments:

"** For clarity:
One draw() loop counts as 1 frame so -

In our IF statement:
we are essentially capturing a frame of canvas at the end of every loop of draw() until we have reached the amount of frames we set in ‘var gifLength’, in our case 180 frames. When 180 frames have passed (aka 3 seconds @ 60 fps), stop capturing and save as a .gif file."

Hope this helps!

Cheers,
Mike

6 Likes

I’ve managed to host a CCapture demo sketch online: :star_struck:
https://ThimbleProjects.org/gotoloop/509340
https://ThimbleProjects.org/gotoloop/509340/sketch.js

Important note: the CCapture library demands the file “gif.worker.js” in order to save all of the capture() canvas frames as a “.gif” file! You can grab 1 at the link below: :robot:
http://cdn.JsDelivr.net/npm/gif.js/dist/gif.worker.js

“index.html”:

<!DOCTYPE html>

<meta charset=utf-8>
<meta name=viewport content=width=device-width,initial-scale=1>

<script defer src=https://cdn.JsDelivr.net/npm/ccapture.js></script>

<script defer src=https://cdn.JsDelivr.net/npm/p5></script>
<!--<script defer src=https://cdn.JsDelivr.net/npm/p5/lib/addons/p5.dom.min.js></script>-->
<!--<script defer src=https://cdn.JsDelivr.net/npm/p5/lib/addons/p5.sound.min.js></script>-->

<script defer src=sketch.js></script>

“sketch.js”:

/**
 * CCapture Bouncing Colorful Balls Demo (v1.0)
 * GoToLoop (2018-Jun-26)
 *
 * https://Discourse.Processing.org/t/
 * how-to-save-your-p5-js-sketch-as-a-gif-using-ccapture-js/1264/2
 *
 * https://ThimbleProjects.org/gotoloop/509340
 * http://CodePen.io/GoSubRoutine/pen/KaerGb/right/?editors=101
 *
 * Get the "gif.worker.js" file from:
 * http://cdn.JsDelivr.net/npm/gif.js/dist/gif.worker.js
 */

"use strict";

const NUM = 15, balls = Array(NUM),

      FORMAT = 'gif', WORKERSFOLDER = './',
      VERBOSE = false, DISPLAY = true,
      FPS = 60, FRAMERATE = FPS, FRAMELIMIT = 5 * FPS,

      capturer = new CCapture({
        format: FORMAT, workersPath: WORKERSFOLDER,
        verbose: VERBOSE, display: DISPLAY,
        framerate: FRAMERATE, frameLimit: FRAMELIMIT
      });

let bg;

function setup() {
  createCanvas(400, 400).mousePressed(restart);
  frameRate(FPS).noStroke();

  bg = color(random(0xd0, 0x100), random(0xd0, 0x100), random(0xd0, 0x100));
  for (let i = 0; i < NUM; balls[i++] = new Ball);

  capturer.start();
  console.table(capturer);
}

function draw() {
  background(bg);
  for (const b of balls)  b.display().update();
  capturer.capture(canvas);
}

function restart() {
  bg = color(random(0xd0, 0o400), random(0xd0, 0o400), random(0xd0, 0o400));
  for (const b of balls)  b.initBall();
}

class Ball {
  static get VEL() { delete this.VEL; return this.VEL = 2; }
  static get MIN_RAD() { delete this.MIN_RAD; return this.MIN_RAD = 5; }
  static get MAX_RAD() { delete this.MAX_RAD; return this.MAX_RAD = 30; }

  constructor() {
    this.pos = createVector(), this.vel = createVector();
    this.initBall();
  }

  initBall() {
    const r = this.rad = random(Ball.MIN_RAD, Ball.MAX_RAD), v = Ball.VEL;
    this.pos.set(random(r, width - r), random(r, height - r));
    this.vel.set(random(-v, v), random(-v, v));
    this.c = color('#' + hex(~~random(0x1000), 3));
    return this;
  }

  update() {
    const { pos, rad } = this;
    pos.add(this.vel);
    pos.x > width  - rad | pos.x < rad && (this.vel.x *= -1);
    pos.y > height - rad | pos.y < rad && (this.vel.y *= -1);
    return this;
  }

  display() {
    fill(this.c).ellipse(this.pos.x, this.pos.y, this.rad<<1);
    return this;
  }
}
3 Likes

@GoToLoop How do you use this gif.worker.js. I didn’t see it in your sketch.js. How do you make it available in your code?

Kf

Put the gif.worker.js in a folder within your project folder and then refer to that folder in the CCapture constructor.
i.e.
Lets say the folder you put the gif.worker.js in is called ‘worker’ - in that case the constructor should look like this at the workersPath parameter: (its all in my video :slight_smile: )

      capturer = new CCapture({
        format: 'gif', 
        workersPath: './worker',
        verbose: true,
        framerate: 60 

      });
2 Likes

There are 3 files I’ve hosted at https://ThimbleProjects.org/gotoloop/509340: :smile_cat:

  1. https://ThimbleProjects.org/gotoloop/509340/index.html
  2. https://ThimbleProjects.org/gotoloop/509340/sketch.js
  3. https://ThimbleProjects.org/gotoloop/509340/gif.worker.js

All 3 inside the root folder “509340/”. :sunglasses:

2 Likes

Thimble site is retiring on 2019/Dec/16. So all links won’t work after that! :disappointed:

All my sketches from Thimble are hosted at Glitch now: :fish:

And my CCapture.js demo sketch is more precisely here: :video_camera:

1 Like

Hi, Thanks for the tutorial.
I am using CCapture, but I’m putting my draw code in a module so I have to instantiate p5 object

I used the versbous option, so I know the capturere starts, but once it’s called, my draw function won’t be invoked any more.

var canvas

// capturer.start();

let sketch = new p5(function(p5){

let p0, p1, p2;

p5.setup = function() {

        p5.capturer = new CCapture( { format: 'gif', workersPath: './js/', verbose: true } );

        capturer.start()

Whoops I deleted my post :sweat_smile: The way I’ve always done it was to put my capturer.start() in my draw:

function draw () {
  // Start recording
  if (!capturer.__isRecording) {
    capturer.__isRecording = true
    capturer.start()
  }

  // Draw things

  // Capture the frame
  capturer.capture(canvas)
}