A better way to write Web Assembly

For the last year I’ve watching the the tooling of the web assembly world evolve, primarily from the perspective of a Rust developer. What has concerned me is my inability to escape JavaScript at every turn. Not that I dislike JavaScript, I’ve programmed JavaScript for 10 or so years, but more so that when I imagine the future of web development, I imagine it being free from what unnecessary as much as possible.

“Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away.”
— Antoine de Saint-Exupery

Lately I’ve become increasingly inspired by two specific ideas:

For those who don’t know what these are, they are two major ideas about the bridge between web assembly and the browser. Web IDL is a specification that defines the API offered by the browser (how we see our environment when we program JavaScript). Host bindings are a planned feature for web assembly, which might include some type of implementing of Web IDL accessible to web assembly modules.

Currently, there are several offerings for communicating with the browser DOM from web assembly by means of narrow code generation for specific technologies. What I would like to do is instead focusing on a non-specific technology for generating a static API for talking with browser DOM.

When a web assembly module is loaded, we have the opportunity to give it a number of functions to communicate back to the browser’s JavaScript context.

WebAssembly.instantiate(bytes, { 
env: {
functionA: function(){ ... },
functionB: function(){ ... }
}
});

What I intend to do is take the entire set of WebIDL and generate a complete set of functions to interact with every aspect of the DOM.

For instance, taking Console.webidl

namespace console {
void assert(optional boolean condition = false, DOMString message);
void clear();
void log(DOMString message);
...
}

can be turned into:

{ 
console_assert: function(condition, message_start, message_len) {
let _message = stringFromMemory(message_start, message_len);
console.assert(condition, _message);
},
console_clear: function() {
console.clear();
},
console_log: function(message_start, message_len) {
let _message = stringFromMemory(message_start, message_len);
console.log(_message);
},
...
}

With a complete set of functions representative of all interactions with the DOM, a static API could be offered to web assembly modules in any language. This method would not depend on any kind of technology specific code generation, simply one generation technique from Web IDL to JavaScript. The same API could be used by various languages:

C:

extern void console_log(int msg_start, char msg_len);

int main() {
char *greeting = "Hello world!";
console_log(greeting,11);
return 0;
}

Poetry

export_memory "memory"
import "env" "console_log" log 2 0

export "main" main
var msg = "hello world!"
log (address_of msg) (size_of msg)

Rust

#![no_main]
use std::ffi::CString;
use std::os::raw::c_char;

extern "C" {
fn console_log(start: *mut c_char, len: usize);
}

pub fn log(msg: &str) {
let s = CString::new(msg).unwrap();
let l = msg.len();
unsafe {
console_log(s.into_raw(), l);
}
}

#[no_mangle]
pub fn main() -> () {
log("hello world!");
}

Let’s take this one step further. JavaScript has had a historical advantage in how it’s loaded inside of a browser.

A simple script tag was all that was needed to both load and give JavaScript full access to the DOM’s capabilities:

<script src="helloworld.js"></script>

I want to offer web assembly the same capabilities and have created a new project called

It offers the simplicity the of the script tag

  • loading and configuring a web assembly module
  • exposing the complete web IDL
  • calls the main function ( or whatever entry function you like configurable with its attributes )
<script src="https://unpkg.com/webidl-loader@0.0.6/webidl-loader.js"></script>
<webidl-loader module="helloworld.wasm"></webidl-loader>

This custom element helps a lot in getting away with as little JavaScript as possible to get your web assembly going!

My hope is that one day this will be completely unnecessary with proper standards, but until host bindings come I hope to accelerate my own web assembly mad science experiments with this tech, or maybe just enjoy the pleasure more easily of not writing web assembly in Rust all the time. I mean, that’s kind of the dream of web assembly for many people. Being able to write the web in any language. I hope this project gets us just a bit closer until we have something official.

On a completely side tangent, I highly encourage people to check out a fun little language for writing web assembly I came across in the creation of this project.

The whole language can be summarized in two wiki pages and the compiler is super simple. It gave me a bit of joy and hope it does for you too. Have fun out there.