DEV Community

Cover image for Code Experiment: Converting Canvas Animations To Gifs
Melissa McEwen
Melissa McEwen

Posted on

Code Experiment: Converting Canvas Animations To Gifs

I thought I'd try a fun experiment where I show how I approach solving a problem. The idea here is you can see how I work and how messy it is. At each step of the way, I'll show the code I was working with, even if it was messy.

These are in the form of Glitch "remixes", basically easily creating a new copy at each step so the old one has what I was doing before.

You'll notice a lot of code that makes very little sense. A lot of code commented out. And lots of console.log. This is basically how I learn something I'm unfamiliar with. By poking around and seeing what happens.

My process is basically some mixture of:

  • Search for things on Google, Glitch, Codepen and Github
  • Run them myself to see how they work
  • Comment out code to break it to see what matters and what doesn't
  • Add some stuff to see if it works
  • Lots of logging variables at different stages to see what happens to them
  • Trying to combine different examples

Recently I inherited a project with a fairly simple premise. Or at least I thought so. The idea was to take an animation on HTML canvas and turn it into a cool animated gif. That way you could easily share it on social media. Full disclosure I don't know anything about canvas animation except that it exists.

Turns out there is a lot I don't know. SHOCKING.

surprise pikachu

Also turns out that browsers don't really want you to do that? I should have remember this excellent article The GIF Is Dead. Long Live the GIF.

The point of it is that web standards sticklers have been trying to bury the gif for ages. So they aren't exactly eager to build in gif making abilities to web browsers. Meanwhile everyone and their mom thinks animated stuff on the web = gifs.

But there are some cool things built in to web browsers that I suppose are meant to tempt me to abandon gifs. One is the MediaRecorder Web API.

A lot of people seem to use it for webcam recording, but I couldn't find many examples that utilized canvas animations. I found one on GitHub called WebRTC samples Record stream from a canvas.

It's using a 3d example but I wanted to use some 2d animation so I ported it to Glitch and tried it out.

Next I stumbled upon this simpler CanvasRecorder implementation on Github. I loved it because it really was just one simple readable implementation of the concept.

GitHub logo SMUsamaShah / CanvasRecorder

Record HTML5 canvas to webm video with JavaScript

CanvasRecorder.js

HitCount

Record a canvas to webm video without effecting rendering performance.

NOTE: Only tested it with Chrome and should work fine with Firefox

Blog article: https://smusamashah.github.io/blog/2018/10/26/CanvasRecorder

How to use

Create a recorder

const canvas = document.getElementById('animation');
const recorder = new CanvasRecorder(canvas);
Enter fullscreen mode Exit fullscreen mode
// optional: bits per second for video quality, defaults to 2.5Mbps
const recorder = new CanvasRecorder(canvas, 4500000);
Enter fullscreen mode Exit fullscreen mode

Start recording

recorder.start();
Enter fullscreen mode Exit fullscreen mode

Stop recording

recorder.stop();
Enter fullscreen mode Exit fullscreen mode

Save/download recording

recorder.save();

// Save with given file name
recorder.save('busy_motion.webm');
Enter fullscreen mode Exit fullscreen mode

How it works

It is based on this WebRTC sample. Captures MediaStream from a canvas element and records it with MediaRecorder.




Ah now that's a great example! As simple as possible so you can understand it easily and play around with it.

And it worked great. Perfect. I'm done!

Oh just kidding. It makes a webm not a gif. The web standards people really love the webm but I'm not sure anyone has gotten the memo. You can't share them on Twitter. Oh and it doesn't work in Safari at all. Sigh.

I detoured a bit here exploring Giphy's API, should be capable of converting webm to a gif since the site is. It also has nice social sharing features. But that doesn't solve the Safari or mobile issue. I also learned the hard way that the upload api won't take webm. If I ever need to record an A-frame WebVR scene I've got a start here.

So on to various other "solutions". Gif.js seems OK. But a lot of the examples I want to use are in CoffeeScript and I really just can't read them very easily.

I don't know her

Me when I see CoffeeScript

As I said, I like it when examples are simple and in just plain ol' Javascript. It's more likely people are using Javascript than CoffeeScript. Also it's usually easier to go from plain Javascript to CoffeeScript than vice versa.

Thank goodness I found this codepen that includes it in a simple example. Though hilariously it's for demonstrating a problem with gif.js

I get it working on top of my old one that's nice

At this point the fans on my computer come on and I'm worried it might fly away.

my laptop getting ready to fly

It says "Works in browsers supporting: Web Workers, File API and Typed Arrays" hmm. Time to test on mobile, which is easy since it's on Glitch.

I'm shocked but it works!! Also works in my mortal enemy AKA Safari AKA the new IE. That's cool.

On to the next candidate. ccapture. But I read the docs and it uses gif.js sooooo I'm gonna bet it's gonna use more resources and my fans are already getting a workout. I skip it.

Now I could take a detour into generating a gif server side but I've already spent more time on this than I planned so I will sadly leave that. Plus I have some experience with node "canvas"...and it isn't the same as "real" canvas.

Next up is jsgif but I skim the docs and immediately give up.

Basically the conclusion I've come to is all the solutions are bad and should be banned.

Looks like gif.js is the "best" solution.

I'm curious to see what it can do. Like the final app may use just "regular" canvas but I've also thought about using WebGL. I have a bunch of A-frame examples lying around and A-frame is Three.js which is WebGL.

Sadly it says TypeError: Argument 1 of CanvasRenderingContext2D.drawImage could not be converted to any of: HTMLImageElement, SVGImageElement, HTMLCanvasElement, HTMLVideoElement, ImageBitmap.

I hack around a bit but eventually I realize I'm spending too much time on this (that's why I use a timer when I work because I can easily get sucked into tangents).

Well uh maybe it's A-frame's fault. I know there is a nice Three.js example on Glitch so I'll see if that will work.

Same error. But I remember CCapture had some cool 3D gifs so maybe it needs something in there. Now here I have to note that on projects like this that aren't high priority I'm not going to dive into things deeply and figure out how they work. If you're building something you want to last for years, you might want to figure out how this stuff works on a deeper level. I'm not.

At this point I'm not even bothering with a UI. If you wanna try it out. You can use start() and stop () in the browser console. It works!

Now I do something v bad and try A-Frame even though why??? The initial example I tried was rather complex and my computer now feels like it might be on fire?

computer is on fire

I had to force quit the browser and then I tried it with a simple example and it worked!

Nice but this isn't what I'm supposed to be doing. Oops.

So I think the gif.js "solution" is the best one. I cleaned up the app so I can work on it later (or my colleague can) or you can "remix" it and do something cool.

Canvas example from this [Codepen](https://codepen.io/agar3s/pen/pJpoya)

I hope to do another post where I work on the final product which I promise is cooler.

Since this is my first time doing this kind of thing, I'd love to know your tips and tricks!

Top comments (2)

Collapse
 
mergerg profile image
Raphael

This is a good post, but the amount of embeds with the full pages made it really slow. Maybe next time you could add screenshots instead? Or even videos that don't autoplay

Collapse
 
dongguangming profile image
dongguangming

cool, maybe use