Refactored Telegram

It’s all just ones and zeros under the cover

Building and Serving Go WASM Projects

One of the advantages of maintaining a blog is that it works as a good destination to record how to do something that you find yourself needing to do occasionally, but keep forgetting how to do it. Documenting the process in public, and in a way that will hopefully help someone else that needs to do the same thing, is a laudable goal in itself, but it does raise the question on why one would do so when there are many, many, other blog posts on exactly the same topic. At least this way, there is a reference that I know exists, and is written in a way that works for me, specifically because I wrote it myself.

With this preamble out of the way, here is what you need to know in order to build a WASM project in Go, and execute it in a browser1.

Building A Go WASM Project

WASM targets are treated like any other architecture and OS target in Go, and are built using the same way, by setting environment variables. In order to build a WASM artefact, you will need to set GOOS to js, and GOARCH to wasm:

GOOS=js GOARCH=wasm go build

WASM from Go is still in development, which results in a few limitations. The WASM artefacts are reasonably large; and some niceties that are available to JavaScript, like HTTP request streaming, are not available. But the projects do work, and the standard library is reasonably well supported.

Executing the WASM Project In a Browser

Once you have a WASM artefact, it can be served by a HTTP server and executed within the browser. In order to do this, some bootstrapping is required:

  1. The WASM file will need to be served with the content type application/wasm. Go’s file server in the standard library will set this for you automatically, but other web-servers or application platforms, like Google App engine, might need to be configured accordingly.

  2. Within the HTML page loading the WASM file, you will also need to load a JavaScript bootstrap file that is included in the Go distribution at $GOROOT/misc/wasm/wasm_exec.js. This is specific to the version of Go, so you may need to redeploy it when upgrading the version of Go (at least in my experience).

  3. Finally, to execute the WASM file itself, you’ll need to include this JavaScript fragment:

    <script>
      // Assuming the WASM artefact is served at '/main.wasm'
         
      const go = new Go();
      WebAssembly.instantiateStreaming(fetch("/main.wasm"), go.importObject).then((result) => {
        go.run(result.instance);
      });
    </script>
    

Doing this will launch the main function of your Go project.

Along with access to the DOM APIs, there are some (possibly meant to be undocumented) techniques for passing information upon startup of the WASM application. From looking at the contents of “wasm_exec.js”, I’ve found the following attributes on the created go instance in the fragment above:

  • go.argv: a JavaScript array of arguments that will be available to the Go application through os.Args. Argument 0 will be set as if it was the command name, but additional arguments can be added using standard array methods like push().
  • go.env: a JavaScript object that can be used to set environment variables for the Go application.

I’m not sure to what degree these are supported. They seem to be present in “wasm_exec.js” of at least two versions of Go; but there doesn’t seem to be any documentation about them, so the usual caveats around working with undocumented features apply here.


  1. It’s also possible to build and run Go WASM projects in Node, but this is not something that I’ve found myself needing to do. ↩︎