WebAssembly in Practice – Speeding Up Image Processing

Luiz TanureLuiz Tanure
2 min read

Note: This article was originally published on June 20, 2019. Some information may be outdated.

WebAssembly lets us run compiled code from C, C++, or Rust in the browser. It opens up a new world for front-end developers needing performance for heavy tasks.

In this post, we’ll use WebAssembly to process an image--resizing or applying a grayscale filter--and compare it to a JavaScript version.

Why Use WebAssembly for This?

  • JavaScript is single-threaded and not optimized for CPU-heavy operations.
  • WebAssembly is compiled and runs closer to native speed.
  • You can reuse existing C/C++ libraries with little overhead.

Step 1: Compile the C Code to WebAssembly

This is a simple image grayscale function written in C.

// grayscale.c
#include <stdint.h>

void grayscale(uint8_t* data, int size) {
  for (int i = 0; i < size; i += 4) {
    uint8_t avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
    data[i] = data[i + 1] = data[i + 2] = avg;
  }
}

Compile it using Emscripten:

emcc grayscale.c -O3 -s WASM=1 -s EXPORTED_FUNCTIONS='["_grayscale"]' -o grayscale.js

This gives you:

  • grayscale.js - glue code to run the module
  • grayscale.wasm - compiled binary

Step 2: Load the WASM Module in JavaScript

const wasm = await fetch('grayscale.wasm').then(res => res.arrayBuffer());

const module = await WebAssembly.instantiate(wasm);
const grayscale = module.instance.exports.grayscale;

// Now grayscale() can be called with an image buffer

Step 3: Compare with JavaScript

Here’s a JS version:

function grayscaleJS(data) {
  for (let i = 0; i < data.length; i += 4) {
    const avg = (data[i] + data[i+1] + data[i+2]) / 3;
    data[i] = data[i+1] = data[i+2] = avg;
  }
}

And the WASM call is:

const buffer = new Uint8Array(imageData.data.buffer);
grayscale(buffer, buffer.length);

Performance Results

Tests show:

  • WASM is 2-5x faster on large images
  • Less impact on main thread
  • Easier to scale for more filters or real-time edits

This example shows how WebAssembly brings native performance to the web, unlocking use cases that were once too slow in JavaScript.


Originally published at letanure.dev

0
Subscribe to my newsletter

Read articles from Luiz Tanure directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Luiz Tanure
Luiz Tanure