tsowamainasara@gmail.com

The JS developer's view of WASM in 2019


A look at WASM from the javascript developer's perspective

18 February, 2019


Webassembly is a great innovation but the majority of modules are made with C++, Rust or GO, and as javascript developers having to maintain two codebases for one project is hard, fortunately that is where AssemblyScript comes in, assemblyscript compiles typescript to wasm and this opens up webassembly to all javascript developers.

Before assemblyscript arrived we had to learn C++ before we could use webassembly but now we have the means of getting into the webassembly ecosystem.

To better understand this environment we will need to ask ourselves a few questions.

1: What can we gain from webassembly?

A huge plus we get from webassembly is the performance boost, webassembly is up to 32x faster than javascript and this allows javascript apps to run at near-native performance.

Native apps made with javascript are often criticized for being slow and unresponsive, especially in low power devices despite the constant performance improvements being shipped by the developers, so having webassembly will allow these apps to run a lot faster.

2: What can we do with webassembly?

There are a lot of things that can be done with webassembly, some of which include

This list can go on for a while but that will make this post too long, so here is a link to its use cases.

3: How is support for webassembly?

Webassembly has a very active community behind it, there are a lot of developers working on great projects like.

This means our modules can also fit into a bigger picture and practically run everywhere

4: How do we get started?

The only way to get started is by using assemblyscript and it is very easy to set up.

Step 1: install

We install assemblyscript by running

$ npm i AssemblyScript/assemblyscript -g

This installs a few commands.

$ asc -h
SYNTAX
  asc [entryFile ...] [options]

EXAMPLES
  asc hello.ts
  asc hello.ts -b hello.wasm -t hello.wat
  asc hello1.ts hello2.ts -b -O > hello.wasm
$ asinit -h
Sets up a new AssemblyScript project or updates an existing one.
For example, to create a new project in the current directory:

  asinit .

#####Step 2: Initialize project

To initialize a project we run

$ asinit my-project

This creates a new project for us, but we have to manually install dependencies

$ cd my-project && npm install

#####Step 3: Build

The asinit command takes care of adding build scripts to our project so all we have to do is run

$ npm run asbuild

Examples

1: Pointers

Web assembly uses a stack-based virtual machine and this means it uses an array-like structure called a stack to store variables, a problem with this is that we will eventually run into pointers and javascript does not play well with pointers.

Scenario: We have a wasm library compiled from assemblyscript that returns a very important string.

export function return_string(): string{
    return "Important stuff";
}

And we use it in our javascript file like this

const wasmModule = await WebAssembly.instantiateStreaming(fetch('simple.wasm'), {})
const importantResult = wasmModule.instance.exports.return_string();

console.log(importantResult); //=> 8

The code above will run with no issues but what gets logged in the console is not the string but a pointer to the string and if we look at our memory, we will find that it looks like this.

"\0f\00\00\00I\00m\00p\00o\00r\00t\00a\00n\00t\00 \00s\00t\00u\00f\00f";

Instead of loading the wasm binary directly via WebAssembly.instantiateStreaming, we can load it with assemblyscript itself and that will help us with those pesky pointers.

import { instantiateStreaming } from "assemblyscript/lib/loader";
const wasmModule = await instantiateStreaming(fetch('simple.wasm'), {})
const importantResult = wasmModule.getString(wasmModule.return_string());

console.log(importantResult); //=> Important stuff

Example 2: DOM

As a javascript developer, using the DOM is essential but webassembly has no straight forward way of doing this, so different languages have different ways of getting around the problem.

auto root = emscripten::val::global("document").call<emscripten::val>(
      "getElementById",
      std::string("root")
    );
var root = js.Global().Get("document").Call("getElementById", "root")
let document = web_sys::window().unwrap().document().unwrap();
let root = document.get_element_by_id("root").unwrap();

We can also achieve this but it will take a bit more effort. The first thing we need to do is provide the module a method it can use to manipulate the DOM.

import { instantiateStreaming } from "assemblyscript/lib/loader";
const wasmModule = await instantiateStreaming(fetch('simple.wasm'), {
    index: {
        changeDiv: (id, value)=>{
            var div = document.getElementById(wasmModule.getString(id));
            div.innerHTML = wasmModule.getString(value);
        }
    }
})

Then use it in our module

declare function changeDiv(id: string, value: string): void;
export function set_hello_message(): void {
    changeDiv("helloDiv","Hello from WASM");
}

##Conclusion

WASM is fast, safe and reliable so having a way to use it without leaving our comfort zone is very encouraging.

#Thanks for reading!!!!

The original version of this post was, in all honesty, a very sour look at the situation and I am glad @jtenner pointed that out. I hope this shows a better view