Build WebAssembly Apps in Go
Initialize Project
go mod init go-wasm
go mod tidy
Hello World
package main
import "fmt"
func main() {
fmt.Println("Go WebAssembly")
}
Build for WebAssambly
GOOS=js GOARCH=wasm go build -o main.wasm
Run Application (Node.js)
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
npm install -g live-server
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Go WebAssembly</title>
<script src="/wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("/main.wasm"), go.importObject)
.then((result) => {
go.run(result.instance);
})
</script>
</head>
<body>
</body>
</html>
Start live-server
and visit localhost:8080
:
Manipulate JS-DOM
package main
import (
"fmt"
"syscall/js"
)
func main() {
fmt.Println("Go WebAssembly")
document := js.Global().Get("document")
hello := document.Call("createElement", "h1")
hello.Set("innerText", "Go WebAssembly")
document.Get("body").Call("appendChild", hello)
}
GOOS=js GOARCH=wasm go build -o main.wasm
Start live-server
and visit localhost:8080
:
Expose Functions
package main
import (
"fmt"
"syscall/js"
)
func exposedFunction(this js.Value, inputs []js.Value) interface{} {
fmt.Println("Exposed Function Executed!")
return nil
}
func main() {
// channel to keep the wasm running
c := make(chan int)
fmt.Println("Go WebAssembly")
js.Global().Set("exposedFunction", js.FuncOf(exposedFunction))
document := js.Global().Get("document")
hello := document.Call("createElement", "h1")
hello.Set("innerText", "Go WebAssembly")
document.Get("body").Call("appendChild", hello)
// value for c is never send
<-c
}
Function can be called from your browser console:
Optimizing Filesize
TinyGo is a new compiler for the Go programming language. TinyGo focuses on compiling code written in Go, but for smaller kinds of systems:
- The Go compiler and tools (from golang.org) are the reference implementation of the Go programming language. They are primarily intended for server side programming but also used for command line tools and other purposes.
- The TinyGo project implements the exact same programming language. However, TinyGo uses a different compiler and tools to make it suited for embedded systems and WebAssembly. It does this primarily by creating much smaller binaries and targeting a much wider variety of systems.
TinyGo can be installed on a variety of host systems. On Arch you can get the compiler directly through Pacman:
sudo pacman -S tinygo
tinygo build -o main-tiny.wasm -target wasm ./main.go
ls -la
310393 Dec 26 20:05 main-tiny.wasm
2065184 Dec 26 19:47 main.wasm
When running the new, tiny version of our program we get an Type Error:
TypeError: import object field 'wasi_unstable' is not an Object
This is because we copied in the following file from the Go compiler:
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
But here we need to swap it for the version from TinyGo:
cp $(tinygo env TINYGOROOT)/targets/wasm_exec.js ./wasm_exec_tiny.js
And link it into our HTML file instead.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Go WebAssembly</title>
<!-- GO COMPILER -->
<!--
<script src="/wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("/main.wasm"), go.importObject)
.then((result) => {
go.run(result.instance);
})
</script> -->
<!-- TinyGo COMPILER -->
<script src="/wasm_exec_tiny.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("/main-tiny.wasm"), go.importObject)
.then((result) => {
go.run(result.instance);
})
</script>
</head>
<body>
</body>
</html>