WebAssembly: byte-code of the future
Tags: javascript, webassembly, webdev
Published: 2023-08-14 14:41:03

---
title: WebAssembly: byte-code of the future
published: true
description: What if browsers supported all programming languages?
tags: javascript, webassembly, webdev
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x1vmauwspaccuvmany92.png
# published_at: 2023-08-14 13:45 +0000
---

Ever since Netscape launched JavaScript, there were some developers that liked it and others that didn't.

Regardless of which side you're on, I think we can all agree that **it would be good if browsers supported more programming languages**.

This is the promise of WebAssembly: Providing a **generic runtime** that any programming language can compile to.

## Past Attempts

In the earlier days of the web, extensions were attempted with Java Applets and Microsoft ActiveX. But both were plagued by security issues and eventually dropped. The problem was they executed without access controls, which became a massive attack surface.

Later, Macromedia Flash and Silverlight had some success, but eventually met the same tragic fate. They both lacked an open standard, which made it hard for browser and OS vendors to support.

## What is WebAssembly?

**WebAssembly** (aka WASM) is an [open standard](https://www.w3.org/groups/wg/wasm) byte code format that works in all browsers. It's a low-level binary format and execution engine, conceptually similar to Oracle's JVM or Microsoft's CLR.

It's designed from the ground up to be hosted and safe. It **cannot** access the machine's memory or hard drive. Only the host can decide what APIs to expose.

WASM is a portable format, so it can support many programming languages. Think Rust, Ruby, Python and even JavaScript can be compiled to WASM byte code.

Though it was originally designed to target the browser, it works well outside the browser too.

It can run on the server, in the cloud, in hardware devices, or used a plugin system.

## Writing WASM

There are several ways to create a `.wasm` file:

- Write it by hand. (not recommended)
- Write it with [Wasm Text Format](https://webassembly.github.io/spec/core/text/index.html).
- Use a higher level language like AssemblyScript, Rust, Ruby, etc.. and then compile it.

I'll show you a few examples:

### Wat is WAT?

The WASM specification provides a text-based format for defining WASM modules that is called WAT (WAsm Text format). It uses S-expresions, similar to Lisp or Clojure.

Here's what a basic module looks like:

```lisp
; define a module
(module
  ; define a function called "add"
  ; it takes 2 params:
  ; - $a is a 32-bit integer
  ; - $b is a 32-bit integer
  ; it returns an 32-bit integer
  (fun add (param $a i32) (param $b i32) (result $i32)
    ; load param $a onto the stack
    local.get $a
    
    ; load param $b onto the stack
    local.get $b
    
    ; perform 32-bit integer "add" operation
    i32.add
    
    ; the last value on the stack is returned
    ; which is the result of the `i32.add`
  )
)
```

The `.wat` file can be compiled to a `.wasm` using `wat2wasm` which is part of the [WebAssembly Toolkit CLI tools](https://github.com/WebAssembly/wabt):

```sh
# outputs example.wasm
> wat2wasm example.wat
```

Now the `.wasm` can be executed from any host. It can even be executed from the command line using `wasmtime`:

```sh
# invoke "add" function, and pass args 1,2
> wasmtime example.wasm --invoke add 1 2
3
```

### AssemblyScript

There is also a higher-level language called [AssemblyScript](https://www.assemblyscript.org). It's like TypeScript for WebAssembly.

If we re-write the `add()` function from the previous section with AssemblyScript, it would look like this:

```typescript
// in add.ts
export function add(a: u32, b: u32): u32 {
  return a + b;
}
```

As you can see, it's much more readable now.

To compile it, use AssemblyScript's compiler `asc`:

```sh
pnpm install -D assemblyscript
pnpm run asc add.ts --outFile=math.wasm
```

### Comparing formats

To compare AssemblyScript to WAT, I built a little tool:

https://assemblyscript-play.vercel.app

You can also use the CLI `wasm2wat` to compare formats:

```sh
# outputs .wat format
wasm2wat math.wasm
```

## Runtime execution

Just like there are many ways to compile wasm, there are many ways to execute it too.

### Using WebAssembly in the browser

To use the WebAssembly API in the browser, first load the assembly:

```javascript
// fetch .wasm file
const response = fetch('/path/to/some.wasm')

// instantiate module with streaming
const module = WebAssembly.instantiateStreaming(response)
```

Then, call one of the exported functions:

```javascript
const result = module.instance.exports.add(1, 2)
```

An optional API can be passed into the module too:

```javascript
// fetch .wasm file
const response = fetch('/path/to/some.wasm')

// instantiate module and pass an api
const module = WebAssembly.instantiateStreaming(response, {
  imports: {
    // share console.log
    log: console.log
  }
})
```

### Using WebAssembly on the server

WebAssemblies can be executed on the server too. The API is virtually identical to the browser.

The only difference is that instead of fetching the `.wasm` from a server using a URL, it can be read from the disk using `fs.readFile()`:

```javascript
import fs from 'fs'

// read .wasm file
const bytes = await fs.promises.readFile('/path/to/some.wasm')

// instantiate the module
const module = WebAssembly.instantiate(bytes)
```

Then, call one of the exported functions, just like we did in the browser:

```javascript
const result = module.instance.exports.add(1, 2)
```

It's also possible to do this from many other languages. For example [rust](https://docs.rs/wasmer/latest/wasmer), [ruby](https://github.com/wasmerio/wasmer-ruby), [python](https://github.com/wasmerio/wasmer-python) or from the [CLI](https://wasmtime.dev).

### Using WebAssembly in the Cloud

Another big use-case for WASM is the cloud.

It has some advantages over JavaScript cloud functions:

1. **No cold starts**: The host only has to load a `.wasm` file instead of full app. Typical JS apps have many files to load, which takes a long time.
1. **Faster deploys**: All that gets uploaded is a simple binary.
1. **Polyglot hosting**: All languages that compile to WASM can be deployed to the cloud without requiring any special runtime.
1. **Snapshots**: Execution can be snapshotted. For example, an app that does expensive work during initialization can be snapshotted. Then future requests can start with the snapshot, eliminating the expensive startup time.

A great example of WASM in the cloud is [Fermyon](https://www.fermyon.com). It's like AWS Lambda but for WebAssembly.

To use Fermyon, install their CLI [spin](https://developer.fermyon.com/spin/install).

Then create a new project:

```sh
# create a new spin project
# template is "http-js"
# project name is "spin-example"
spin new http-js spin-example
cd spin-example

# install dependencies
npm install
```

Then define a handler in `src/index.js`:

```javascript
const encoder = new TextEncoder()

export async function handleRequest(request) {
  return {
    status: 200,
    headers: { "content-type": "text/plain" },
    body: encoder.encode("Hello World").buffer
  }
}
```

To run in dev mode:

```sh
spin watch
```

To deploy to The Cloud™, run `spin deploy`:

```sh
spin deploy
```

Notice how that deploy was instant?

## Gotchas

There are still a couple rough edges of of WASM:

- WebAssembly is still kind of new and in active development. Though it is improving rapidly.
- Full support is not yet available for some programming languages.
- WASM doesn't have basic data types like strings or a standard library. This is by design. Languages are expected to be provide their own standard library.
- Because a "standard library" needs to live inside your `.wasm`, it can make file size large.

Most of these will be resolved with time.

## The future

Over the past few years WebAssembly has made a lot of progress. 

Eventually all languages will have compilation targets and runtimes for hosting it (if they don't already). This will enable all languages to run in the browser, server, or even in hardware.

It might also bring on new types of programming languages that are designed for a WebAssembly-first world.