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
-
Data Science.
-
Graphic Design.
-
Game Development.
-
Virtual Reality.
-
Platform Emulation. There is a Windows 2000 virtual machine that is written in webassembly
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.
-
ASM-DOM: A webassembly virtual DOM for building C++ SPAs
-
Blazor: An experimental web UI framework using C#/Razor
-
Yew: A rust framework for making web apps
-
go-vdom-wasm: An experimental virtual DOM for golang and WASM
-
Asmble: Webassembly to JVM bytecode compiler
-
Wasmer: A standalone JIT WebAssembly runtime A similar project is wasmjit
-
pywasm: A webassembly interpreter written in python
-
wac: A webassembly interpreter written in C. Supports SDL!
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: This is the assemblyscript compiler, it compiles typescript files to wasm binaries.
$ 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: Sets up a new assemblyscript project or updates an existing one
$ 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.
- C++ (emscripten)
auto root = emscripten::val::global("document").call<emscripten::val>(
"getElementById",
std::string("root")
);
- GO (syscall/js)
var root = js.Global().Get("document").Call("getElementById", "root")
- Rust (wasm_bindgen)
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