This repo contains examples of how to work with WebAssembly and WASI in the Go ecosystem.
Regular WASM is meant to be executed inside a browser (like Chrome or Firefox) or JavaScript runtime (like Node, Deno or Bun). As such it only has access to web APIs or runtime-specific APIs.
WASI on the other hand, as the name "WebAssembly System Interface" suggests, provides more direct access to the host system, like to the filesystem or network sockets. Similar to how the browser executes JavaScript and WebAssembly in a sandbox, WASI runtimes also execute WASI programs in a sandbox, requiring explicit permissions for things like file access.
This works with Go versions prior to 1.21, as it's "regular" WASM, not WASI.
Compile the Go program to WebAssembly: GOOS=js GOARCH=wasm go build -o go-wasm.wasm
Go WASM execution requires a Go-specific wrapper, wasm_exec.js
. This can then be executed by any JavaScript runtime, in or outside the browser. The Go project provides this in $(go env GOROOT)/misc/wasm/wasm_exec.js
.
The non-browser runtimes differ in how files are read from the host's filesystem, so for reading the wasm_exec.js
file, in addition to this file itself, we need to have another runtime-specific wrapper.
For running the WASM program in the browser, you can serve the files with any web server that supports the application/wasm
MIME type. You can use a regular Go server (http.ListenAndServe(...)
), Caddy, or others.
cp go-wasm.wasm browser
cp $(go env GOROOT)/misc/wasm/wasm_exec.js browser/wasm_exec.js
cd browser
caddy file-server --listen :2015
Then in your browser visit http://localhost:2015.
Tested with Firefox 116.0.3
cp go-wasm.wasm node
cp $(go env GOROOT)/misc/wasm/wasm_exec.js deno/wasm_exec.js
cd node
node node.js
The Go authors also provide a convenience script for this, so instead of copying the wasm_exec.js
and writing our own node.js
wrapper script, we can simply run:
$(go env GOROOT)/misc/wasm/go_js_wasm_exec go-wasm.wasm
Tested with Node v18.17.1
cp go-wasm.wasm deno
cp $(go env GOROOT)/misc/wasm/wasm_exec.js deno/wasm_exec.js
cd deno
deno run --allow-read=./go-wasm.wasm deno.js
ℹ️: Deno runs programs in a sandbox by default, so for reading the WASM file we need to grant the permission accordingly.
Tested with Deno 1.36.1
cp go-wasm.wasm bun
cp $(go env GOROOT)/misc/wasm/wasm_exec.js deno/wasm_exec.js
cd bun
bun bun.js
Tested with Bun 0.7.3
🚧 TODO
Go 1.21 has official WASI preview1 support, so we can compile Go programs to WASM that execute outside of a browser / JavaScript runtime.
Compile Go program to WASI: GOOS=wasip1 GOARCH=wasm go build -o go-wasi.wasm
Any WASM runtime with WASI support, on any OS, can then execute this file:
- wasmtime:
wasmtime go-wasi.wasm
(tested with wasmtime CLI 11.0.0) - Wasmer:
wasmer run go-wasi.wasm
(tested with Wasmer 4.1.1) - wazero:
wazero run go-wasi.wasm
(tested with wazero 1.4.0)
Or we run the WASI program with an embeddable WASM runtime from within another Go program. Most of the ⬆️ listed runtimes also have Go libraries for embedding.
cp go-wasi.wasm embed-wasi/wazero
cd embed-wasi/wazero
go run .