Fast GIF parsing on the web with WASM + Wuffs
Tags: webassembly, wuffs, gif
Published: 2018-06-28 10:25:47

---
title: Fast GIF parsing on the web with WASM + Wuffs
published: true
description: The fastest way to decode GIFs in your browser or anywhere JS code runs, is via Web Assembly. Learn how 👍
tags: wasm, wuffs, gif
cover_image: https://thepracticaldev.s3.amazonaws.com/i/z4piur9avqankac842ji.gif
---

GIFs, or JIFs, are one of those strange oddities of the internet. The format is slow, inefficient, and from the 80s (!)- did we even have color screens then?

Yet today, they're everywhere. But if you want to *work* with this format on the web—rather than just displaying it in an `<img>`—the best practice is to read a GIF's raw bytes and [parse it using JavaScript](https://www.google.com.au/search?q=parse+gif+javascript).

This is admittedly esoteric, but pure JavaScript is slow compared to doing it in native C, as there's just a lot of low-level byte manipulation and work to do. How do we get the best of both worlds? Web Assembly, or WASM. In this post, I'll show you how to parse directly in the browser using WASM, which gives about a 2x speed increase over JS only.

To do this, we'll use Wuffs: a library for Wrangling Untrusted File Formats Safely. It generates modern and _provably_ safe C code for dealing with multimedia or other encoded file formats. You can [read more about it here](https://github.com/google/wuffs).

# The Demo

Here's `fastgif`, a library to decode GIFs based on Wuffs and WASM; and on the right, we use a popular JS-only GIF library. The time to parse the GIF is the key, and Wuffs on a modern MacBook is typically about 2x the JS-only version.
(Of course, you might not see such amazing results, but `fastgif` is faster in most environments. You can also try another browser! 🤞😅)

{% codepen https://codepen.io/samthor/pen/EROaaJ %}

## Take It Home Today

If you'd like to use `fastgif` in your site, [check it out here](https://github.com/samthor/fastgif). The library and demo work in all modern, evergreen browsers: Edge, Safari, Firefox and Chrome. Neat! 🤖📸

## The Numbers

![Speeds of fastgif vs other approaches in a log graph](https://thepracticaldev.s3.amazonaws.com/i/mhgvwke6e2bbowewlcch.png)

<small>nb: this is a log graph. Smaller is better.</small>

Some thoughts-

* On the web, `fastgif` is almost exactly the same speed as a native, unoptimized binary of Wuffs—the C program running from a command line.
* When we use `-O3` to compile the native binary, it speeds up enormously—those optimizations seemingly don't apply to WASM.
* The second example, with just a single frame, is _very_ fast for the native case: this hints that moving lots of bytes around is costly for the web.
* There's also a bit of startup/parse time that's encapsulated in the web-based approaches. Repeated decodes would probably be faster. 

Nonetheless, `fastgif` is probably the fastest way to decode GIFs on the web. As an aside: this sort of work is best suited for a `Worker`, off the main thread—but that's out of scope of this post.

## Want To Know More?

If you'd like to learn more about how `fastgif` takes Wuffs and makes it speedy ⚡💨 for the web, read on!

<hr />

The rest of this post is for those folks who:

* Have a good understanding of JavaScript
* Have a basic knowledge of C and the command-line
* Want to port any native library—not necessarily just Wuffs—to the web

## Step Zero: fetch Emscripten

While you can write WASM files by hand or with other tools, the main toolchain to build Web Assembly is Emscripten. You can follow [its install instructions here](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html).

Once you're setup or you want to resume coding, be sure to source the `emsdk_env.sh` for your platform:

```
# Linux/macOS
source ~/Desktop/path/to/emsdk/emsdk_env.sh

# Windows
C:\path\to\emsdk\emsdk_env.bat  # or .ps1 for PowerShell
```

Emscripten needs certain versions of Node, Clang and some other environment variables to be set. This also means you can set up Emscripten without installing it as root, as it won't replace anything on your system.

## Step One: build a naïve demo

Wuffs, like many other native C libraries, has some demo applications—those with an entry point of `int main()`. This will read input from the command line, and output it to the shell. This doesn't map to what we want to do on the web, but it's a good place to start.

In Wuffs' case, there's a few examples under `example/`. I started by trying to just compile the GIF player, which normally outputs ASCII art to the terminal.

```bash
git clone git@github.com:google/wuffs.git
cd wuffs/
source ~/Desktop/emsdk/emsdk_env.sh

# finally, compile:
emcc -s WASM=1 -o gifplayer.html example/gifplayer/gifplayer.c

# and run a quick webserver of choice:
python -m SimpleHTTPServer
serve
```

This will generate `gifplayer.html`, `gifplayer.js` and `gifplayer.wasm`. This is Emscripten being helpful, mostly for debugging and getting started—now, if you open up `http://localhost:5000/gifplayer.html` in your browser, you'll see:

![Assertion failed: TOTAL_MEMORY not big enough for stack](https://thepracticaldev.s3.amazonaws.com/i/grj5ebztqsz1e8zih0a5.png)

... actually, we see an error message. 💥

Some quick Googling 🔍 later, and it looks like we need to allow the program more memory. Let's recompile:

```bash
emcc -s WASM=1 -s TOTAL_MEMORY=128MB -o gifplayer.html example/gifplayer/gifplayer.c 
```

Great! Open now, and we'll see this:

![Waiting for input](https://thepracticaldev.s3.amazonaws.com/i/42cud6y82p01nhj1x2hy.png)

When you ask for input using e.g. `scanf` or reading from the command line, Emscripten will by default use the JavaScript method `prompt()` to ask for data. Try pasting in some text—the browser will continue prompting until you _Cancel_ the input, which counts an EOF.

Unfortunately, even copying and pasting raw GIF bytes into this form will do.. nothing. If you do this and read the browser's console, you'll see a message about "gif failed parsing header".

So the code is running- yay 🎉! But since we can't give it real bytes, only a JavaScript string, nothing happens 🙅.

## Step Two: Send raw bytes to the GIF decoder

As `gifplayer.c` has an `int main()` method in C, it's going to run something- waiting for input- when we start it. While it's a good place to start, I want to be able to pass it raw bytes—perhaps from a `window.fetch` or AJAX request.

Let's modify it to do so.

### a. Remove main method

I can open up the `gifplayer.c` file and modify it, removing the `main` and `fail` methods just by commenting them out:

```c
/*
int fail(const char* msg) {
  ...
}

int main(int argc, char** argv) {
  ...
}
*/
```

### b. Replace reading from input, with accepting a passed buffer

Let's comment out the `read_stdin` method, and add a `read_buffer` method to replace it.

```c
/*
const char* read_stdin() {
  while (src_len < SRC_BUFFER_SIZE) {
    ...
  }
  return "input is too large";
}
*/

// add this method
const char* read_buffer(uint8_t *buf, size_t len) {
  src_buffer = buf;
  src_len = len;
  return NULL;
}
```

However, `src_buffer` used to be a fixed sized buffer based on `SRC_BUFFER_SIZE`. This bounded the amount of data read from the command line. Instead, we're going to accept a pointer to somewhere in memory. Let's update the declaration of `src_buffer`:

```c
// don't need a fixed buffer, now just a pointer
//uint8_t src_buffer[SRC_BUFFER_SIZE] = {0};
uint8_t *src_buffer;
size_t src_len = 0;
```

### c. Recompile and pass data

Now, let's recompile and pass in data via the `read_buffer` method directly in our JavaScript console. To expose a method to JS properly, we need to indicate it in our compile pass. We'll also need the `play` method to display the output.

```bash
emcc -s WASM=1 -s TOTAL_MEMORY=128MB \
    -s EXPORTED_FUNCTIONS="['_read_buffer','_play']" \
    -o gifplayer.html \
    example/gifplayer/gifplayer.c
```

Ok, so now let's re-open the Emscripten window. Nothing happens and no relevant error messages appear in the console—we're no longer doing anything in `main()`.

However, we can run the `Module._read_buffer` method to load our buffer in. By default, the Emscripten toolchain exposes these methods on the global `Module`. It also exposes the C standard library—methods like `malloc` and `free`.

So to test, there's a few steps. We need to get the source of the image, we need to `malloc` it some memory, pass that to `read_buffer`, and then play it. The following code does just that—so **paste it into the JavaScript console**:

```js
// the following base64 string is really long- just copy and paste this whole section
// it's the base64 encoded version of:
// https://raw.githubusercontent.com/google/wuffs/master/test/data/muybridge.gif

const testGifB64 = "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";
const buf = Uint8Array.from(atob(testGifB64), (c) => c.charCodeAt(0));
const at = Module._malloc(buf.length);
Module.HEAP8.set(buf, at);
Module._read_buffer(at, buf.length);
Module._play();
```

With any luck, you should see a decoded, moving ASCII art horse 🐎💨 playing in your console:

![Horse in ASCII art in console](https://thepracticaldev.s3.amazonaws.com/i/tvooq4nem16st0b32nep.png)

Congrats! At this point, I rewarded myself with a donut. You should do the same. 🍩

## Step Three: Don't display to the screen

Right now, we just play the ASCII art to the console. Emscripten even implements the `usleep` method with a _busy loop_: the GIF player, which pauses between frames, actually just loops to block the browser from proceeding. Yuck!

Instead of this, let's get access to the actual frames. First, inside the `play()` method, comment out anything to do with sleep and displaying to the screen:

```c
#ifdef _POSIX_TIMERS
    /*
    if (started) {
      ...
    }
    */
#endif

    //ignore_return_value(write(stdout_fd, print_buffer, n));
```

Now, we want to use the `EM_ASM_` macro, which lets us call JavaScript inline inside C code. This is purely magic, which takes the string inside the macro and puts it Emscripten's output JS helper. So the rest of the method should now be:

```c
    // .. continued from above

    cumulative_delay_micros +=
        (1000 * wuffs_base__image_buffer__duration(&ib)) /
        WUFFS_BASE__FLICKS_PER_MILLISECOND;

    // .. add this bit
    EM_ASM_({
      onframe($0, $1, $2);
    }, print_buffer, n, cumulative_delay_micros);

    // TODO: should a zero duration mean to show this frame forever?
  }
  return NULL;
}
```

Finally, be sure to add the Emscripten header at the top of the file, as now we're using new macros:

```
#include <emscripten.h>
```

You can now recompile using the same command as before:

```bash
emcc -s WASM=1 -s TOTAL_MEMORY=128MB \
    -s EXPORTED_FUNCTIONS="['_read_buffer','_play']" \
    -o gifplayer.html \
    example/gifplayer/gifplayer.c
```

And reload the page. Now, run our helper blob again, but this time provide an `onframe` method:

```js
const frames = [];
const decoder = new TextDecoder();
function onframe(buf, len, micros) {
  const s = decoder.decode(Module.HEAP8.slice(buf, buf + len));
  frames.push({s, ms: micros / 1000});
}

const testGifB64 = "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";
const buf = Uint8Array.from(atob(testGifB64), (c) => c.charCodeAt(0));
const at = Module._malloc(buf.length);
Module.HEAP8.set(buf, at);
Module._read_buffer(at, buf.length);
Module._play();
```

Once you've run the above code, be sure to log the `frames` object, like this.

![Frames in JavaScript console](https://thepracticaldev.s3.amazonaws.com/i/r1k5nbodvnotzmwxncl1.png)

Phew! Now, you could modify them, print them out, or use them at your leisure. This definitely isn't perfect, because you need to add `onframe` to your global scope (!). But, Emscripten's helper libraries are already pretty bad at this—they're already adding `Module` to your window.

## Step Four: Get the image bytes

The demo we've been fixing actually just renders ASCII art to the console. Let's actually simplify the code and just get the raw bytes of the GIF.

Let's update the `gifplayer.c` to pass more, varying arguments to the `onframe` JavaScript method:

```c
// update the forward declaration at top
extern void onframe(uint32_t *, uint32_t, uint32_t, uint32_t);

// -- removed for brevity --

    // update EM_ASM_
    EM_ASM_({
      onframe($0, $1, $2, $3);
    }, dst_buffer, width, height, cumulative_delay_micros);
```

Now, recompile the code—just using the same command as before, hit ⬆️ in your terminal.

In your browser, reload and run this snippet of code to generate `ImageData` instances, enough to render for us:

```js
const frames = [];
const decoder = new TextDecoder();
function onframe(buf, width, height, micros) {
  const len = width * height;
  const buf = new Uint32Array(len);
  buf.set(Module.HEAPU32.subarray(buf, buf + len))
  const clamped = new Uint8ClampedArray(buf.buffer);
  const imageData = new ImageData(clamped, width, height);
  frames.push({imageData, ms: micros / 1000});
}

const testGifB64 = "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";
const buf = Uint8Array.from(atob(testGifB64), (c) => c.charCodeAt(0));
const at = Module._malloc(buf.length);
Module.HEAP8.set(buf, at);
Module._read_buffer(at, buf.length);
Module._play();
```

Finally, run this code to display `frames[0]` to the screen:

```js
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
const context = canvas.getContext('2d');
context.putImageData(frames[0].imageData, 0, 0);
```

Congratulations! You made an image appear! 🖼️🎉

There's still, obviously, a few stepping stones from what we've just done to creating a usable library like you saw in `fastgif`, before.

One challenge is that Emscripten's helper layer is quite enormous (~100kb of JS, more HTML), and is really designed for monolithic programs—where all your code is in C, rather than just trying to wrap up a single library.

If you'd like to read more about Emscripten and how to avoid using its helper layer, its "runtime", [check out my follow-up post here](https://dev.to/samthor/emscriptens-compiled-web-assembly-used-minimally-4fd4). It's way more technical than this post, and that's saying something—some knowledge of C required.

<hr />

# Done

I hope you've learned either:

a) that WASM is cool, and can speed up traditionally complex computational tasks like GIF decoding: and/or
b) that it's not too hard to port a native C library—albeit in in a very basic way—to the Web.

Thanks for reading! ✨