This might be familiar to those who know, but I tried it out myself, so here is a quick note.

  • The demo image is borrowed from here. No specific license was mentioned, but I will replace it if there are any issues.

For the library, I used jsgif.

The basic flow is to load the library, draw each frame onto a canvas, add it to the library, and once finished, generate a GIF file from the binary data.

Here is a slightly more detailed walkthrough:

  1. Load LZWEncoder.js, NeuQuant.js, and GIFEncoder.js
  2. Prepare a canvas of the appropriate size
  3. Create an encoder instance from GIFEncoder
    var encoder = new GIFEncoder();
  4. Configure animation settings such as frame delays on the encoder
    encoder.setRepeat(0);
    encoder.setDelay(100);
    encoder.setSize(120, 120);
  5. Start writing frames
    encoder.start();
    1. Draw the image onto the canvas
      canvas.drawImage(img);
    2. Add the frame to the encoder by passing the canvas context
      encoder.addFrame(ctx);
  6. Finish writing
    encoder.finish()
  7. Output the binary data from the encoder and create a Blob
    var bin = new Uint8Array(encoder.stream().bin);
    var blob = new Blob([bin.buffer], {type: ‘image/gif’});
  8. Create an Object URL from the Blob and display it (don't forget to revoke it)
    var url = URL.createObjectURL(blob);  
    var image = new Image();  
    image.src = url;  
    image.onload = function() {  
    URL.revokeObjectURL(url);  
    };

You can find the demo around here.

* Firefox is recommended for this demo. In Chrome, due to a bug where images may not actually be loaded upon img.onload, some frames in the animation may be skipped. While there are workarounds like using Ajax to fetch images, I opted for loading them via src for readability in this demo. That said, it could just be a bug in my own code, so if that's the case, please let me know quietly :)

Someone pointed it out on Google+ (not so quietly, though!). It turned out to be completely my misunderstanding. How embarrassing...

Source code is here.

References: