C in browser — Experiments with WebAssembly

C in browser — Experiments with WebAssembly

I like experimenting with new technologies and also a lover of C/C++ programming language. I was listening about WebAssembly for past few years like 1 or so and was trying out different methods to make a working demo of C wasm file in Browser but recently I came to know about a npm module or library that will help to make simple with Emscripten. If you don't know about webassembly take a look at my previous article in this series. Why and How webassembly?

We are going to use library called webassembly which is a npm module which provides a C header in which WebAssembly functionality is provided by a C header. A small JavaScript support library (distributions) provides the browser runtime.

I created a repo in my Github name is wasm-experiments with sub-folder js-webassembly. I tried creating a simple calculator with WebAssembly. Let’s see it code:

#include <webassembly.h>
#include <math.h>

export double add(double a, double b) {
    return a + b;
}

export double substract(double a, double b) {
    return a - b;
}

export double multiply(double a, double b) {
    return a * b;
}

export double divide(double a, double b) {
    return a / b;
}

export double mod(int a, int b) {
    return a % b;
}

export double square_root(double a) {
    return sqrt(a);
}

In C code is imported webassembly.h which is provided by the library and exporting each function like we normally do in Javascript or Typescript. To compile we run wa compile -O -o program.wasm program.c . The library provides CLI for easy use where we can give output path with -o and input C program with -O being flag for optimization.

const wasm = require("webassembly");

const loaded = wasm.load("./program.wasm");

loaded.then(module => {
    console.log("1 + 2 = " + module.exports.add(1,2));
    console.log("1 - 2 = " + module.exports.substract(1,2));
    console.log("1 X 2 = " + module.exports.multiply(1,2));
    console.log("1 / 2 = " + module.exports.divide(1,2));
    console.log("1 % 2 = " + module.exports.mod(1,2));
    console.log("Square root of 9 = " + module.exports.square_root(9));
})
.catch(err => {
    console.log("Error loading wasm file:", err);
})

Then in javascript I just used single library webassembly to load the wasm file which return a promise which resolves to c module and we can now directly use it the exported function from javascript using module.exports.<function_name> .

This was the easiest part, I wanted to use same wasm file in browser also. So I created a loader script for wasm.

function loadWebAssembly(filename, imports) {
    return fetch(filename)
        .then(response => response.arrayBuffer())
        .then(buffer => WebAssembly.compile(buffer))
        .then(module => {
            imports = imports || {};
            imports.env = imports.env || {};
            imports.env.memoryBase = imports.env.memoryBase || 0;
            imports.env.tableBase = imports.env.tableBase || 0;
            if (!imports.env.memory) {
                imports.env.memory = new WebAssembly.Memory({ initial: 256 });
            }
            if (!imports.env.table) {
                imports.env.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' });
            }
            return new WebAssembly.Instance(module, imports);
        });
}

Below script is written in index.html which loads the wasm file and exposes exported functions to globally.

if ( !( 'WebAssembly' in window ) ) {
  alert( 'You need Webassembly enabled browser' )
}

var cadd, csubs, cmultiply, cdivide, cmod, csquare_root;

loadWebAssembly( './assets/wasm/program.wasm' )
  .then( instance => {
    var exports = instance.exports;
    console.log( '1 + 2 = ' + exports.add( 1, 2 ) );
    console.log( '1 - 2 = ' + exports.substract( 1, 2 ) );
    console.log( '1 * 2 = ' + exports.multiply( 1, 2 ) );
    console.log( '1 / 2 = ' + exports.divide( 1, 2 ) );
    console.log( '1 % 2 = ' + exports.mod( 1, 2 ) );
    console.log( 'Square root of 9 = ' + exports.square_root( 9 ) );

    cadd = exports.add;
    csubs = exports.substract;
    cmultiply = exports.multiply;
    cdivide = exports.divide;
   cmod = exports.mod;
   csquare_root = exports.square_root;
} );

Following code is in main.js which uses C program defined functions to calculate sum, difference etc.

const result = document.querySelector('#result');
const czero = document.querySelector('#czero');
// ... varibales for reference in document

const controls = [result, czero, cezero, positive_negative, sqrt, one, two, three, four, five, six, seven, eight, nine, zero, add, multiply, mod, divide, equals, subs];

// Adding event listner
controls.forEach(c => {
    if (c !== 'result') {
        c.addEventListener('click', (e) => {
            handleDisplay(e);
        });
    }
})

function handleDisplay(e) {
  // ... code to handle display in result
}

// function using exported from c
function calculate(text) {
    if (text.includes("+")) {
        const operandsArray = splitText(text, "+");
        return cadd(operandsArray[0], operandsArray[1]);
    } 
    if (text.includes("-")) {
        const operandsArray = splitText(text, "-");
        return parseInt(csubs(operandsArray[0], operandsArray[1]));
    }
    if (text.includes("*")) {
        const operandsArray = splitText(text, "*");
        return cmultiply(operandsArray[0], operandsArray[1]);
    } 
    if (text.includes("/")) {
        const operandsArray = splitText(text, "/");
        return cdivide(operandsArray[0], operandsArray[1]);
    } 
    if (text.includes("%")) {
        const operandsArray = splitText(text, "%");
        return cmod(operandsArray[0], operandsArray[1]);
    }
}

function splitText(text, operand) {
  // ... code to split expression for operation
}

Head over to Github to look at full code.

wasm-experiments

Here is link to video demo.

Hope you like it. More experiments coming soon.

Source

Happy learning, Happy coding!