Introduction to WebAssembly with Go

Let's offload heavy computation on browser to WebAssembly with Go

WebAssembly Overview

What is actually WebAssembly? Here is a snippet from webassembly.org official website.

"WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications."

OK that sounds mouthful, basically WebAssembly is open standard technology that allows application to be written in high level language such as Go, Rust, and C++ and then compiled into portable binary code and execute it in web browser.

Unlike Javascript, we can ship application within one single *.wasm binary file. This means distribution of the application will be faster over the network and broswer can cache the binary.

Use Cases

There still not a lot of use cases to use WebAssembly, but a notable example is when application require operations with 64-bit integers data type. WebAssembly can overcome limited support for 64-bit integers in Javascript as Javascript doesn't have native 64-bit integers, it only support 53-bit integers. Meanwhile, Go have native 64-bit integers support.

Maximum value for an integer with 64-bit is 18,446,744,073,709,551,615 according to Wolfram Alpha

WebAssembly can be used to build performant web application that requires heavy workload beyond Javascript capabilities. Figma uses WebAssembly and achieve 3x faster load albeit it's built with C++ not Go.

Browser Compatibility

Top 4 (Chrome, Firefox, Edge and Safari) browser have perfect compatibility for WebAssembly, while Internet Explorer doesn't support WebAssembly at all. Opera somewhat compatible with WebAssemly but not fully. Here is the detail on Browser Compatibility according to Mozilla.

Go Support for WebAssembly

Go 1.11 added an experimental port to WebAssembly. Go 1.12 has improved some parts of it, with further improvements starting in Go 1.13.

Compiling to *.wasm

Application written with Go can be compiled to WebAssembly by specifying GOOS=js and GOARCH=wasm environment variable. Let write some simple Go code. Prepare a workspace with following structure:

introduction-to-web-assembly-with-go
├── go.mod
├── public
├── server.go
└── wasm
    └── main.go

Add content to main.go

package main

import "fmt"

func main() {
    fmt.Println("Hello, Go WebAssembly")
}

And then compile it to WebAssembly

$ GOOS=js GOARCH=wasm go build -o ./public/app.wasm ./wasm/main.go

Javascript Support File

Official documentation for WebAssembly in Go mention we need to add Javascript support file to load the WebAssembly binary onto the web page. This Javascript file can be copied from GOROOT if you already install Go.

$ cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" ./public/

HTML Boilerplate

Add simple HTML boilerplate to tie up everything together.

<html>
<head>
    <meta charset="utf-8" />
    <script src="wasm_exec.js"></script>
    <script>
        const go = new Go();
        WebAssembly.instantiateStreaming(fetch("app.wasm"), go.importObject).then(
            (result) => {
                go.run(result.instance);
            }
        );
    </script>
</head>
<body></body>
</html>

Create simple static HTTP server on server.go

package main

import (
    "fmt"
    "net/http"
)

func main() {
    err := http.ListenAndServe(":9090", http.FileServer(http.Dir("public/")))
    if err != nil {
        fmt.Println("Failed to start server", err)
        return
    }
}

Run go run server.go and point your browser to localhost:9090 and open browser console, you should see "Hello, Go WebAssembly" printed.

Example Code

Simple WebAssembly code can be found in this GitHub repository. The file structure should be as follow:

introduction-to-web-assembly-with-go
├── go.mod
├── public
│   ├── app.wasm
│   ├── index.html
│   └── wasm_exec.js
├── server.go
└── wasm
    └── main.go

Limitations

While WebAssembly is awesome, but there are some limitations. For example, due to browser strict security policy we cannot write application that interact with the filesystem. I would not go into this too deep, it might worth another post.

Closing

WebAssembly provide versatile solution to offload heavy computation on web pages. This could be applied to games, mathematical calculator, data processing, or even video editor on the web. In my opinion more website will be using WebAssembly in the future. WebAssembly will not replace Javascript for the near future as the ability to interact with DOM still hard to write. Next, I will write on how to interact with WebAssembly from Javascript. Stay tuned!