loading...

How to run c code in angular / javascript - WebAssembly

vishesh profile image vishesh ・4 min read

Its exciting and cool to do such a thing. You’ve probably been hearing about WebAssembly and WebComponents for a long time. Here we will see about the WebAssembly. Below I will show you how to start using the compile C code, call it directly from the Angular component.

What is web assembly

WebAssembly is a low-level assembly-like language with a compact binary format that runs with near-native performance and provides languages such as C/C++, C# and Rust with a compilation target so that they can run on the web. It is also designed to run alongside JavaScript, allowing both to work together.

I had to run a audio processing functionality that is written in c in an angular application so that the processing is done in real time. Thus I had to use this WebAssembly concept.

Steps

  1. For using it in angular or in plain JS, we initially need to install emscripten's emsdk. This SDK compiles the c code into js.

Open you terminal and enter below commands to install. This is a local installation. I am installing within the project root folder so i can keep all stuff in one git repo.

$ git clone https://github.com/juj/emsdk.git
$ cd emsdk
$ ./emsdk install latest
$ ./emsdk activate latest
  1. In the tsconfig.json/app.tsconfig.json change the module from es2015 to esnext.
    Alt Text

  2. Time to write some c code. Below is a simple fibonacci code. Write it and save as fibonacci.c in the project directory. I choose to store the files under wasm folder. This way it is clean and easy to understand.

/wasm/fibonacci.c

#include <emscripten.h>

int EMSCRIPTEN_KEEPALIVE fibonacci(int n)
{
    if (n == 0 || n == 1)
        return n;
    else
        return (fibonacci(n - 1) + fibonacci(n - 2));
}

EMSCRIPTEN_KEEPALIVE - This is to mention to the compiler to export this function. Thus you can import this function in js.

  1. Our c program is ready and now we need to run the magic command that converts it into a js. On terms its called wasm (web assembly modules).

If you execute emcc in terminal it will say "emcc command not found". For that you need to first run the bash file to import the emcc command line into the terminal.

$ cd emsdk
$ source ./emsdk_env.sh
$ cd ..
$ emcc \
    fibonacci.c \
    -Os \
    -o fibonacci.wasm \
    --no-entry

Final folder structure
Alt Text

Using in Angular

This is where things get messy. I tried several solutions in internet. Especially this https://malcoded.com/posts/web-assembly-angular/. It one looked very much possible. But still i faced multiple issues regarding imports and finally stopped when i got a webpack issue. Angular handles it internally and I don't want to meddle with it.

Simplest way is the best way

As per official document in MDN, we can use instantiateStreaming to get the wasm file and access the exported functions/classes.

But there is a catch to it. We can only get the file using fetch or http call. Which means we need to serve the file from a server. Thus I created a simple node server locally and added the wasm file under public folder.

const express = require('express')
const app = express()
const port = 3000

app.use(express.static('public'))
app.use((req, res, next) => {
  res.set({'Access-Control-Allow-Origin':'*'});
  next();
})

app.get('/', (req, res) => {
  res.send('Hello World!');
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

Finally almost done. Now in the component we can use browser inbuilt WebAssembly object and get things done.

app.component.ts

import { OnInit } from '@angular/core';
declare const WebAssembly: any;

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, AfterViewInit {
  constructor() {}

  ngOnInit() {

    // Logs the function arguments
    const importObject = { imports: { imported_func: arg => console.log(arg) } };

    // Initialise the fibonacci wasm file WebAssembly.instantiateStreaming(fetch('http://localhost:3000/fibonacci.wasm'), importObject)
    .then(obj => {

      // Mail call of the fibonacci function
      console.log('Fibonacci of 30: ', obj.instance.exports.fibonacci(30));

    })
    .catch(err => console.log('fetch error: ', err));
  }

}

Notes

  1. Not supported in IE. Check here for more browser compatibility.
  2. For safari instead of instantiateStreaming you need to use instantiate. Sample code here

References

  1. https://github.com/boyanio/angular-wasm - This is the best sample code you can get. It is also hosted https://boyan.io/angular-wasm/text-to-ascii

Conclusion

This is my experience with webAssembly using c. There is definitely huge scope for this as javascript/typescript has become the main frontend language. More support and easier ways to implement may come down the line. Thus it is a cool stuff to learn now.

If you guys know any other ways or I might have missed anything do let me know in comments. Eager to work on this more.

Posted on by:

vishesh profile

vishesh

@vishesh

A never give up hard working programmer. I am full stack web developer (Angular, NodeJs, PHP, Wrodpress, Devops, AWS EC2, SNS, RDS, Lambda, SES, Cloudfront, etc...).

Discussion

markdown guide