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.

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.

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! 🤞😅)

Take It Home Today

If you'd like to use fastgif in your site, check it out here. The library and demo work in all modern, evergreen browsers: Edge, Safari, Firefox and Chrome. Neat! 🤖📸

The Numbers

nb: this is a log graph. Smaller is better.

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.

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.

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!

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.

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.



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:

... 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:



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

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

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:



/* 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.



/* 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 :



// 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.



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:



// 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 = " R0lGODlhHgAUAIcAAAAAAAEBAQICAgMDAwQEBAUFBQYGBgcHBwgICAkJCQoKCgsLCwwMDA0NDQ4ODg8PDxAQEBERERISEhMTExQUFBUVFRYWFhcXFxgYGBkZGRoaGhsbGxwcHB0dHR4eHh8fHyAgICEhISIiIiMjIyQkJCUlJSYmJicnJygoKCkpKSoqKisrKywsLC0tLS4uLi8vLzAwMDExMTIyMjMzMzQ0NDU1NTY2Njc3Nzg4ODk5OTo6Ojs7Ozw8PD09PT4+Pj8/P0BAQEFBQUJCQkNDQ0REREVFRUZGRkdHR0hISElJSUpKSktLS0xMTE1NTU5OTk9PT1BQUFFRUVJSUlNTU1RUVFVVVVZWVldXV1hYWFlZWVpaWltbW1xcXF1dXV5eXl9fX2BgYGFhYWJiYmNjY2RkZGVlZWZmZmdnZ2hoaGlpaWpqamtra2xsbG1tbW5ubm9vb3BwcHFxcXJycnNzc3R0dHV1dXZ2dnd3d3h4eHl5eXp6ent7e3x8fH19fX5+fn9/f4CAgIGBgYKCgoODg4SEhIWFhYaGhoeHh4iIiImJiYqKiouLi4yMjI2NjY6Ojo+Pj5CQkJGRkZKSkpOTk5SUlJWVlZaWlpeXl5iYmJmZmZqampubm5ycnJ2dnZ6enp+fn6CgoKGhoaKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq6ysrK2tra6urq+vr7CwsLGxsbKysrOzs7S0tLW1tba2tre3t7i4uLm5ubq6uru7u7y8vL29vb6+vr+/v8DAwMHBwcLCwsPDw8TExMXFxcbGxsfHx8jIyMnJycrKysvLy8zMzM3Nzc7Ozs/Pz9DQ0NHR0dLS0tPT09TU1NXV1dbW1tfX19jY2NnZ2dra2tvb29zc3N3d3d7e3t/f3+Dg4OHh4eLi4uPj4+Tk5OXl5ebm5ufn5+jo6Onp6erq6uvr6+zs7O3t7e7u7u/v7/Dw8PHx8fLy8vPz8/T09PX19fb29vf39/j4+Pn5+fr6+vv7+/z8/P39/f7+/v///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQECgAAACwAAAAAHgAUAAAI/+UEmjt3Dl1BhOjSpUOnUN06dQshqlPX0Bw4jOHEjSNHbtzHjuXMlSN3DZs5c+QECuT4UZu3b94yhqNJc+NHcIcGfazZkya2bdy6dcP47Ru4cBiRfhMVJ1jRbtZgHsWITdu2od2mygRntJsiJ4jIdW22yc2uoUa9UTOZTdvVbUG5zd0mTA4UP7myxZqjg4iQXdq4bdNWzRo2bNniXs0mbNgyasu2gTsmxU2TIDxs6IjSLO42w9ee7WK2LNquNitc1Fixw8yuXWOYNImi5pcmJHGsarNmMlSKFFXI1MnB4keSKGHQyKGmiNAZO87GhdM2a7A2aoZpkRnGjVqeFzGGtP84kaNGGlRv1GxxJO6ouHDbsmHLbk1wTG7EmJhQ8WLIiRNmOMEFLO6AqRtuvOlGm2uuqS8boebyZhcaXkiEG1SsWEGGFXy4JZwFrWrwmmqmoeYwt+LCpYtkxFAGHHHECYYFFkQJ5xtuGBzRsGimQfEzakrhJAmhjhqGDFoq+aYb+UwaEZtoTkQMrsmeKQSTT6jhhg5nxLGFmcGyyeaa+RDrsZoG3XpLpmNoaWSPSy4ppZRbctSmzPkajEYaNK1RUzCYwPFGGm2o+oaw+ayxpj5roPHRmmrUVKwbrwD1RkH5EiXxxGpouUWXX3whhplmkhmGmGWWAQaYYpZpphlkllEx5plmjFGGGF10uQULNdoAZJBFKInkkUs2QcWVWVZZpRRYWFGFlFA40UQSRQCxFpCAAAAh+QQECgAAACwAAAAAHgAUAAAI/+PKmTt3Dl06hOgKoiOYEGE6dOoeqouI7lu4ceLIDSRYjhy5jOE+Gks1DmRGkB5BbvsGDly4cOLGZRQX7pu3bt62JRrjDVzLb9+6vfx5bZu2bdy65Qy6dFs2a8XczGnWbdu2atCwaVPajZs1bWG5el3K7em1X0hmiDGbLdmdJpaqXQ1bLZu2bHe57eUWFpu1ZbAwcaJkqxCXFjBaULoWNpu0atasXcOWN9uzVq5o4YpVDByxI0+KxEiRgoULXFu1QZtGTdkuYcaixboyY0iPFzl4pHrmR0mYJnCIqXJipVnYaNSmsVlRxEoiND5m7OjxZEoXPtIM9UkzRpg3m7Aaa/9LTq3Vo2mA79CwUQPHCxgytizagkZMInDcvN3Uhg3bs2mkoaYybK4RBowYYIhhBhhYiOEEF4rAIhpusLlqm7+sYSaa5P6qhppruJmkBiOkmcQHFVx4AYdVutGmGseukUyZZpyZ5kMcp0FDFSp84maWE4Z4wRe/8soQsGegEVC5G4H5AxMtcMmGm2qS6CaaU1SBirICr7lmmWfClGbMMa3pRhpM4oBkmmAw8cabY2jJ0MsukWHGmWeiGROaaKapzBtcFglDkWGw6UUUbaypRkbJqqmGGGXuzBOaJJOzC6ldhunmmEKvycpRR6mhZphklmmmGT2jgUZJaibzMy8vPZ1PphnlRA1QlVl2ASYYW3D5xRZdhhEGGFpo+cUYZIwh5phinBEGmWWSUZYYJdK4g49ECDEEkkk2AcWTUlqJhZRUXFkllVNI8YQwTzYhjJGAAAAh+QQECgAAACwAAAAAHgAUAAAI/9++mUOnTh26de7WqWv3rqE7du8kMoQXr6I7eO/cXcMWjly5cuPKpUNH0CA7dum63Uo3bt3DdyjZqYNWbRu3b968gRtHLtw4c+ZIkvMkpOM5dezWLUSH7pwzadi0bbu57Rs4cOHEkTOXbRCXXefCEVyXTl26c2mfRavGEVu2bd3ACQRHbhqXGEq8nfO27RaqXOjSBS3nDJq0aRzhygWnE1wyT4ISfRp2qAgMKFJ2lSxHrpmyZs+kUWs7ddiwYst4qYpm7MUVGChOzBjypFpnz8yaJYulSxi1Y246lFgRIoULTLSWrBiSwgksRUECbS237JkzNxs+iBhjxQUJGjKKHP+hcodXFjhTsNBqaYzOtc7JnDFDxeOVLVuAVKhI0QKFjB6mCGSLLJzo45ym0kmHHAaNOcY6ZNzaJYgVSoDhBBRmgMGDFHKQIhlzzgnxHAbHOSYZZZSpZhrStMFmERZiiKUMEkbgcAVGQMKNHHF61I0ZZ6KRJhpqrulGnDFi0KYcalQRooQQtpDFG3LGsTKcuZoJDZppWKymmmyeeUEHXrL5hRtRLhnHGl9UwRIrunQj0hqO2rJmm2nssGMPWHipphBxwoHmGHEEEsibbgwjjc5rurwmm6ssgaUXWFDZ5BtuWrmmsW6+6aabbZRxZi3SrqGGGmuw4QYccU7JBCdxuolyBpZwPOWGG6pCZYYZLk+dRppqrIGLTmB24aYbbZ6Ry5tbt9HmWU46keQSWGa5ZZdcdOFFF1tQkaUYYHwhRphYiHEmmWOOMYaYYYgBg4wz0iBDjTXUcGONNvLwYxFLJGnEEEQGYaSSTCx5JJFB+jiEkYAAACH5BAQKAAAALAAAAAAeABQAAAj/38KBCyfOYMFx4saNI1eOHDlxDx8qDLcQ3DdvGbt1w+iNWzdv3y4SBAeOGq9vIrttY6kNG7ZsLLNl01YzG8xt3D5u7OYJhzJt3LBZo1aU2rRpMK8pHWrtGs2a26ZREvNJ29Nr0p5JkwbNa82XQ69Vo1YtGzeW0dzcMLMtW7VrzXixghbNbjSw16xVI1v2WktlmNiUSRWME5kVJVScqhsNWt5rRclOswbT2jFj2H4JOXNDiJEULGQUK0st7LXI046yKtNljBY4XWy54nIjSZEngpaIMHSUGuprwrQ4ucLIjwwTSoo4sTKlELM5cLh06ZSNmSIYwlSjrhasyaFaqiit/wiBg0cKIl/8rIpTx0kabW65CTsqrey0aL74UkPlQQSGFFBAgQchblDCiS1a0eYtmKaRBilqvHKQK2kmKUEEF9gYYQQXTmgiiDR8g0aaaJxpxhm7nHHmGRVXhCYTE1CwyxZLqshhiCcYmUWaZphxZplllOlxGWaYaYZIIKNJwwZijnLwFDaI4cWRW4BMJhlllnnmGWaIPJKZLJ3Zow4+/qgEmUagMSyaZ0RxhhlksEQGmmeCVOZOZZA5JplKPoHGmV4YCUYRV7aSZhNohgTSKziVSQaZZTA75hlEggHzmWT2o5OVEodMphlQHdXzmGKIKcbROixtZpgJvWqGzlGP8UzFl1t62YWXXW6xBRZaYIHFET1kgeWTW2qhpRZbeOFlFlVUQSUVUSSphBNKHjEEkD78yCOQRCRBhJA/HHmkEkkSOaQRRfRQowsvsggIACH5BAQKAAAALAAAAAAeABQAAAj/5s6dQ0cQXcGD6RSaU6fwYEFz5ciNE9ctG7Zw4sZt3Khxo0Ry3MiRC7eLWzhw37px25atWjRo3r6Bo/ntm0ybNMFhsymsAquV2a5dq0btWTNm3bwt9dZtJTduSm9i47YKCZ9r2oZaKyrNGbNl25w6lek0alNv1BRdoJGs2zZs1qQli+YVbNSxTbnpdbpNGqouefZYU5bJDY8XfaI5a9YM79ixUKNmi6YMHLctZCwwWIBgwIFZ0J492yZZmzao25pRKsRoESZArmIJ2VCCgYYYNAbcEU0a6rQqPqY44lSkBA0bOJI0KfMsjJYeHaooS8YFhDBmzFhyyzblUjFSpmh0/7jRYgaNKnhiOZHyYkWxpd0A8VoW9vQ2a+CawuoAQkQMDzyAIYgMfDjBhjy82UasbqDJ7ppsstFmLG2qacKDDVawYoQQSIgABhgyuCUba6zB5ppmlknGGggl3OY0bVRZwYgagJHmF1IMgcEEGvQIZpppqJlGGWWOqWaoa7CJUJtspBGiFUZEycapb7ZZ5hY87ADEGWicQQaZYqph8cQls6GEj214EcOZBaEqsZRsfgGly2SSQaZEE5VkEps0mnGKmUg6gabCUjJh8RpTZmEGmWPCFHOoi04kBRpuUKsGGUeMUZK7oqrBRBljhhGGGmrEfFTMZlTBBptqklGyKBa5klImmmeOAcYXX4ARps5lFk1mmGMcSUUYWkbxhZdbatlFF150qUWWWErR5JJLJqHElFJQKYWUUjwpJZdXTCEFlU48yUQSRxxhxA84upAiiCB2sCEgACH5BAQKAAAALAAAAAAeABQAAAj/48oNNGfu3MFz5sahY5jO4UN16tJFlJgOnTdx4sJl5KixG7ly5MaJlEUNJDmUA0OO2/YN3DeYLsG9zAYunE1wyVCMCnfT5TdvM79xExoTKNBtQr21UmIoW8xu3bZl8+atm7aoWbtx47oNW9Vuj0jIOAVuGzdt1KxBs6aNW7dsXLlGRTs1rtRZbkBFGfbMWKEpZWzY0rbN69u3W7cVtnbNMLZdLi3l4GKCRYwUJbZg06bNMbdt1651xkZNVSZTqVRtCtXLSxEdRHzwePJixa3Ojrc9CzIiyJ9JSVCcMIFCRo8qyvbYsQLECSxriW4k6mwtW7ZrTTLRAvVJxAcaLHbI/0jyhlaXGUOAiConkJSXttY5YwO3tRGFDyhMeODAgkgJF4SQ4QeMbBLnm8WqwQYbqqTKZpIMPAghBzZQEOFCHIawIY1wwLJJG2yiaQybx6wpRQMQXiBjnG6MeWUTRJIgoocwiulsm25Ei4aaazjLxhppDGlhFVWycEmckUYyJxkwMLnGGs6sqWYa667TJhs+rCiGHGSogKmnnjICxxNRNIEyG2yuoYbB67AbwxpxwLlGjGS8kcmmcGJxRpxiJOGsxzXTZPORqqbS5hBhuKlqUWZe8kYZR6bBxpppREsTTUjO2mYacLJp5BXQzlJUMc+oUYuXYpIZRpljimlkFV+IkT4Fl1JYQSQXXnrhBZdehhlGGGCC8WUXXMjAQw4/ECHkEFAkQWSRSSbZxJJKCvFjDz/sqCOPO+IogwwutrAiIAAh+QQECgAAACwAAAAAHgAUAAAI/9/CiQs3UFy3ceTIlTPX0OG5c+giQjznkNw2bt0yevuWDRy4guLEjUvoTRa4cikVkiNIEBu2bNm2YbzWzZu3bt8+Frz149g4kQXDfbvpzZq1a9eyadNWjVvGjeC8qdICiuBHot64Ydx2tFo1azCpxWS6rds2RiN64AqHU6O2bUtnfv169Nq0pDCZVhOlhNSVXdqgbbLSZQ8rbHPBWquLF2nMbNGWecvmycOZCB9KcIBQhNpcamCZRasGrVo0XLF+8fKV6dinLkFeiHBh4kIFCqyyXaM2jVqsERxcgAk05AUNEx0ydJhjDQuWJR5sWDrmZkIimNV64/rCCtcwOC42dP8AkeHBg0HOWJTQcEJTwWx4jljL1luatGrYrP2qICHDBwwwmACBDFwQYYQMVogGp2+i+UWbbKrxbRrtpmlFwAxQkEOKDzIAoYfNPCBjmqVyAgdCaqSJBppoVixFAw8wQMEZcKj5ZRVOHgEkCAuyYIOaxLjRBhtqUnwGmmd6kcIOY1BghCm3ivImk0AowW8xa3yrRkVhhhBFm2BUSOYouGbK5qVsGLGjxfvuC623aBDJZRprGnEkwmrOxOYaCSm8hgxenjmSRRVVrCUUaZCZRphJJKTmGmtSdMYZaO7bRRFblJm0GWgofcaPI4/BL5RdpDEtxWaWaeYZaR6NppJWnGlNpplffPElmFhkeYUWXGzRxQtGWLklllhaecWVVmTpJRdecpGFFVZa+WOQQgZZ5BA9pmXEEk0UCQSRQOBAA4sw3oAjjS60WMIIIHzoISAAIfkEBAoAAAAsAAAAAB4AFAAACP/hxpEjWM6cuXPo0KlTx87hOoft3E10B88ivHfvvIHj+I1jOHHjyhlUiO4cN2Dp0kFkJ7FlO3bbtmmbSbObN4/gxJUjOM7UEnDmVKpcx1ClNWvVsCG9to3bTZwgw7EioosnuXMIEWY9dw0pUqXZtnX75o3st0wkdggbJy5kwZEHsV3zOo1atWvYaD7tFo2Qj1xyaH1b5onNGVm/Cia9Vk1aNLt5s+21y43brSJZZjRxsoOFGYHipo1OlisXL2TSnhUL5swZslbW9rQJo8RKlyUrbvwYppPatGiYJky4YARPixAoSKQQ0cLTLy1r3jC5MuqaoxuiwoX7HU0XHlvKmjn/CbEhw4kZNm6MSvVDSI0morJ+Q5RHZzXH1LJZk2bKAoYPKsiAgwxMgEEIHGb44YdpyDEIG2C2S2oaaaiJZpk+MNiAhCg2GQOEEDpgwYYabpCjm3EGGokjah6DBhpmfvnBAw5EQAaccLRJBhZNVqlFiirWiESaFMcB6bFmnGlGlickWUOGWrrZ6yaQyEnmEXK8WUUncL4J5xlonFlmmCdkeYMMZLrZjy69bvpmHEquAUeYaL4pizBmlkHGD1+USUOauaq5ixqksJmsr1K4mQYYyyxTRhlc/IDlGGMaARMaMCmUxi5r6OqGFmmgOUYbbAw95phTfilmGGIo6VMZZmB8WkaaTfHLyxpNriE1r2tA2eSRQjz5pJNEqsBEFFFIWUSTU2ihpRZbctEll1M+waWWWGR5JQwvsPhiCiuwMIOOMdA4gw48+PCjjzzssCOQQAjxo4412oAjjjoCAgAh+QQECgAAACwAAAAAHgAUAAAI/+EEhhNXsOA4hOPILWSIkFzCceLCgaNYseJAggYLNkOmsaDAbyG9eQsZ0iI4jAIZ5UkpENzIbt1GljT5jeJEcN9KgSl2Eee3mNxkkqRZ0iKnEkaEmawYMubTkVGlEp0mpwcsJ6W2oarDZMkqYd24jYU6deY3odm0tbKxw8OKEx4a4BlLthsyYsuOKaPG7Fcxa9iiBfPWB9CYFiqe3HhQYQSzbdvqcspgAgegQzJY3CihAsWOXM/m0OGT40qkZXogwNKmjWyyVNe4RcOxwUOGEDmGRPmVKsiQDVEeUcS2J1FryTJLvtKAAoSFDBgw1DjixIeLEiGIlaRWbJs2yUKjLuvqgGNFnkxiNmBoUWIGEA81qHlTzi1yXaHdaLXwIKQnSmuIWcWTVGiQI4wugHkqMvDue4YLEFCwpJqTLCqjG3CaAQWSsbTJJptt1OqlklLSUKYkoorqRCRvpHHluw+zCaYTV7ixJBuZnhoqqm4ueWqsYnTB5prA1NpmGVbwUxK/W8ZicJthqMHGmms+1CYWYhhsMDIuv7MlGvA8/LAaMnf5hRlnkpllmGSMSWYZY5QJxphglklGmF0+GYYYXm6BpZZegPFlDz0UkSSUV04hRRRRVrGFF1xiceWVVmKpRZdbaqllllZYOaWUTgICACH5BAQKAAAALAAAAAAeABQAAAj/5gSaK1dQ4DmE6NItVJdOnbp169hNZNduYjty5MaNEzcuIzmDCc+hC6YMncKHENc9XKdRXLhw4mSKyxjyXDk3lBIydMhwJjhw4cDN9GhwnKU+1UKeVMhUptCgMGHK1Bhu0I8ryYqWMzcO4cGXQMF9IwtUqrhjdLjESgLrGyQoVrKAYlbwHNBvY71160Y2L8xq3rxtw1RjTg0jRHiskAPSnGBvsXT5auWqlqpRsIoJwyXLG55CWXIIiZKEBpAiyLh+E2xGBJYiqtrIuNFjBgkZSoIhCwRIDZI2iYQdMnJqnDm/vZSB4/YqRgsaIFQgiXKm2CgmUaCoiWQOnbZEfcgh/ycLk6whFzJGhDBxooSRK1WUDMGR5Ns579uOmbvL2hvrbZqgQYZCVtmEihRSEGKIL5yo4QuQuPqqHLL2oiYTOUogg5RxYhpHml8+6SMKTQ5BYpiiCAKJr22A+eONGXCBCbJvYhJnFFwKUoaMZDzUiBxsQiFkFGLCaIa1brjZRhtuuvmvEaKy8eMYqmTahhtuoEEEGyy38TKbbLzsxhlVoIrpG1eCoUkmMI+5Y5ptstEGzGyuwQYbbbbRZRoapcJFFGJguoYXUaap8xprrlHUmkTvvOTKvQQryxttgpqFFFpW2QUXW3LBpZdbaBFFFVZwqcWTW2aJxRVXbPnll2GKMShmGC7EOKMNNtyQgw485lAjVznksIOPQva4gw825qBjDjjeaMONOQICACH5BAQKAAAALAAAAAAeABQAAAj/37558/YtHLhw4sSNG0eOIUNyEcmVM3euIrpzFrlt3NatoECB4ESC+wYOWbKE4iI2LDeRXDZtMWNu49bNozebHrsRguSNZDig4xQytHbtGjaj2LRto8l0KbZNgZTdLGkQ6EFqWatpvZbNa8xs1gYZIaOMKTeC20aKjCZN2rRqcY9iw5bN6DA/WEhh+YUtFBczezQhK+gtWjRo0ahN42r0mjVn1bBZe4QkU5QyUmrEEHQT2mdWrm6VIuWJ0iJKqFphClWNUaQoRq6MYZIiCJFlOKM9e1aGRA0pk+LsuEFkh4wkVEQle2SpEBVEhJQ9wtFqI+JovYRJQ9YpxYgTHkoU/6myRxYpJ0qk5BnETVy3UIZofn4Gd5q0PylogCBhIsSKIXpAgggdYoCjsHC8uWajZ6B5RhpqeqFlCxlgUOMWVupIoYQdcrCCiBf88AgtnDZyxkFomJGChRS6SMQZbjrCBplSeECjEioEaWqjbmJs5plmmImkBkKEiTEbumDSZpNMNpIGjl+0gckpbZiJ5RNMDFHmscessaaaonIZhRuZoPlDMpiy2aZKZAIxJav7GMsqq2gqOcqxbIYZxZokvVqGmQY/+8wttw4jBZov4wJTm158KWquZf5sphlnKuUNRWRemWZTaraqxqhPknn0mlZOkWUWXHLZhRdfesHFll1kwTfFF19uqUUXYpD5ERpWmKnUmTnk+IOQQxgxRJFEIIEEk042ISUUVlSJhRZbbLnllmpjiQWWWAICACH5BAQKAAAALAAAAAAeABQAAAj/4sYNHCiQ3EFy5cqRIzhOXDiBDsNN9MYN4sRw4MBBFBfR4UNo0MJ9+zYRHMlu3CxmJOmtm7eNG02C82ZoUEVu31xu25bt2rWNOrul5OZN57eTJ1ep6cUtG89t2rJZmzaNmkahRId6M6rz0g8017hh03ZN2jVk0qRFi0by21CV21Ru9YYMTpNFgKIZi4TlSqYwz6xO04mTG9S5Q6lZs/ZN0ppHOnzEMAEjk1VqXLttU7YNWzJcojBpYkVrVKZqn9gU4QHlyIwYOIIwgxYN7rAwdLi40fSmyhYoTqpocfQMEJ4yUtSksqZnRam1cZWVqoVM15EdQWyseEKFNy0nVtg0/ynVTdu2SY/YZpNKVmq1IFmaxLih4sSPF0RY9Lihp2y1arCxprZrsLmGsWtaQQWIH34wBZhFhCDhhBtuOGEF5qiZJpoNnYGmmgOpqQaWJGh4oQrUsOHpmVRYsIEGK9aKBhpmnmlGGWcYg2YaUiixpJhmrNFwMcZOQQOaVuyYBppmmFlGGWSOUUbIWCbRxRpV1GKLLWqk8eUOatAKJBq1nnFGGWWSQcaan66hZhRcomnGw7WkCeaPaA68Jo9prKmmKhqZmbIaPwejcRlmignFkVv8BPCaS5ihRkQNbWTmT7VCisZMZjqd9FMAqwmGlkdDlIZJYHIxRphgdtmFF11ooT3lFVVMScUVVljJBZdAStHFl15okWWVU4xxhRRYXkEllVNA+UQUTlBZpRRSRhmFFE5AOTaUUCAZBA857AgIACH5BAQKAAAALAAAAAAeABQAAAj/3QQO7MaN2zaEB7Vt03Ytm7aH2bBdq0ZtmjRp2jRm48jx2jVrH69NvEaMmUiKFqNBe/Zs5EuR1SpSqyhz2ptDMy1KW/nMmTNrQWUGpVYUo7Rp1KJJQnNr2sWeLJ0xY1ZUZlGLT59Gi/ZMz4020TB2fcbLJ7Nlyixe1XrxKc9aXabgdKZrUhdEUlwtQ3asWNKiWo8i5alsGbNmbfwEKmGiggYhwY4ZM6YVWrNp0YapklTo0KVOjSw1UxQmCQ4iKFawGNGI8l+Mt94YuvIm0p0pVLhYIdMFkzFJabpEmXMsGJgau4wV4xrN1iJMwFgZwfKkhgsxXPCwciWnUZEiuaRB/4NGSlgxYiyhRXsbrQyZLzhqeFCxAwYRHTeMnFrZLK2wYIBxpqVnWILklDuS+MGSYjC5QgMWitDggzd8aoaZZIwBBphfmmnmJ2dcsWSNPs7ARJoCo+klkhZCuMOXC9NChphffOnlsAuV6eQZUJ5ZaSq0msFkBzSgQUuZZJApRhheeNnFQ2eCkcSYsmrxSRlglFHmmDFS4cS/ZJDMMJgmefnwGWTIe6aWXJxpJplZGOlEj1xImQWtMJFJ5phhfGnyTPWcKcaZZQo1DBktRwEmyST70rAXXnpRUyo3j9QSGcqOqeQXZPqarJhigqmxF1902VPPyYjZZRVTREnllFVO8TYkkVZeaWWWWFr5RBFEDkFEF1l8GYaYYIrtJRddarHFFlVM4YSVV04pRRNIEumjjSm4uEKKgAAAIfkEBAoAAAAsAAAAAB4AFAAACP/jypkjiO6cQXTp0qlbh65dO3fv3kWEB88dRIntvokLF07cx3EfyQ00Nw4dOmvT1qljuDLdyZfcvoEDNzPct47ixo0ER47coT7mzp0zh84cOYHlyGHbxq1pN6jevNHE6a1bJTnCdnIMCY5jx2rZsmHLpq2p1G5WvV3rI4TLNXAdpdby9s1qt2jW9Fq7dq3stm1mt91qcyWOH23TZE254+KUNrLaoFHba63aNbJjyzKjVnaMnj45XHRQoeIXtmvWmkWbFg2atGnNYDnCE+iRJEGXqkH6kgNJkhlYvJyos3cZs2ihQIki9EcOGSVBsFwx0yXRNDtdwjBRk+1apR+7+iL/S8aMDw4to94QmQLFBgwrXN6IAmZnkBAjx+yCC2u5GLFjhCFGGmiSeSIJKnToYQQbjvDBiCK8sIIXbrSxcJtqpJGmGGOOYeYZZ1ZJRY8YhOhDmUwWTIGHE1wwZRts9KqmNWeaEUZAY5JZppAnbOCijmCmuYYaXwjJoQZEpLmmGmoIZEYZZJAB5pdefhmmmEty4cOZaJppBkRqYsECGmymgeaZZpRZRplihAmGF15w2QWYYDQJRhhFjEGGGE2KaYYMUqiJ5hlmkkHmGGOKeZOXW3S5xRZeYhEEGGM6uSXKYQQhBAZkQFzmGEOPCaaXXXTRJZZZaKkll1l+CSaYU4a5dZJDRH5Jc5k9hyEmmF1uweXXWnyVExhibjSGGGOOZUWTZ5ZxNtQ2edFll19HCYUVVlRhJZVRRjHFlFJSYcUVRFYxRRVtV2GFFE8ygaQQQxrRIxFDFkGkkUskYcSSSii5JBJKMkGEkD7+4MNgNtAoo4ssstAiIAAh+QQECgAAACwAAAAAHgAUAAAI/96+DRwYTtxBcd4OjhtHrtzDh+YkTpSILdtFbdq2devGjZs1byEHVmMWziRClOKoVbNW7dpLiy+hZcuYEVMcbt/AgSPYM9ozaNGkTZO2khq1ZtVYTrsUqJZHbh2hcuzGjFmzZs+ARuMKLVnQZXmMjGFmDdtZYZtoatyW7BgyZFadXX3G7FhWXXykkBEUDdonJEGQqKqG0RixYsSMJUuG7O0xY8qaCUv2jNgVPG2Q9GAxRIixtcSGDRM2LLEwVXymUHHDhs2fX5uo6GCiREmlOzoAXbs47BcwS6lQIcJzhUmOGUKkhAGDZpmgLnfkfMLmbNWbZmd/+eLFRoWNPmqE/P9w4kNGDyNWBunSgwYMHGrWrmnLZq0lr124JDlqxZ2LiSeUACKEGmaY4QYjwJDjmGmEUsqoW2yRxZZcdLHlllA4OCGIWziZIoQTeujAB1eggeYZoaahZpppYpHFRVxsmYUKEjzoApZklnGmF0poCOIRZ5pxJkgUu2qlFVlkmSXJS8oYwRZfSivmGEpSkOWZZZJRRstmmHHmGWdYaeWVV5KkRZdGUrmjE158OeUROSRBBZlijDHmsWOUYWYZVFTx0xUlbaHlEmL+oGUXUH645BFSihkGscPe0jKZU0pBJRVVXInFllR2ASYWWhihxJdddgnmt2CAAebRYR5DhZRUXHFhZRYKfwkmmF5K2YQ0O30DJpg64ZoSrkwouUQSSyzRRJRSYLHFlUZYUSWVWEfxhJRVUGEFlVE4+cQTT+ZoQw9ACmkEEkk08eQTTiaxpBFHMKFEk0gSQUSRRR5RJBBA/AgkIAA7 " ; 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:

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:



#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:



// .. 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:



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:



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.

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:



// 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:



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:



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. It's way more technical than this post, and that's saying something—some knowledge of C required.

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! ✨