Thoughts of a software developer

26.07.2018 21:24 | Modified 21.09. 21:23
Golang webassembly (wasm) testing with examples

Golang version 1.11 is coming with a Webassembly support. So what is it about, I went a head and tried some.

You can install the current Golang beta from https://golang.org/dl/#go1.11beta2.

In repository https://github.com/jelinden/go-webassembly there are four really simple examples. You can get the repository with go get github.com/jelinden/go-webassembly.

Example 1, simple directory

Simple folder: The simplest example.

simple.go:

package main
func main() {
    println("Hello WASM!")
}

This is compiled wasm with
GOARCH=wasm GOOS=js go build -o simple.wasm simple.go.

Now, this wasm file is served to clients with server.go. In server.go, there is also the index.html.

<html>
    <head>
        <title>Testing WebAssembly</title>
        <script src="wasm_exec.js" type="text/javascript"></script>
        <script type="text/javascript">
            const go = new Go();
            WebAssembly.instantiateStreaming(fetch('wasm/simple.wasm'), go.importObject).then(function(res) {
                go.run(res.instance);
            });
        </script>
    </head>
    <body>
        <h2>WebAssembly content</h2>
        <div id="wasm">Check the console ...</div>
    </body>
</html>

wasm_exec.js is needed to handle go values and running the wasm binary, you can get the current version from https://github.com/golang/go/tree/master/misc/wasm.

If you build the server too go build -o server server.go and run it (in simple folder) with ./server, use browser to access http://localhost:8000, you should be able to read the hello message from browser console (nothing on the actual page yet!).

Example 2, valuetoelement directory

valuetoelement.go:

package main
import (
    "syscall/js"
)
func main() {
    doc := js.Global().Get("document")
    doc.Call("getElementById", "hello").Set("innerHTML", "Hello Wasm!")
}

and server.go html:

<body>
    <h2>WebAssembly content</h2>
    <div id="hello"></div>
</body>

Now, with this example, you should see the hello on the web page. You can build and run it with

cd valuetoelement
bash build.sh && ./server

Example 3, redbox directory

This is basically doing the same thing as example 2, it just draws a red box instead of writing text.

Example 4, spinning directory

In order to make something happen on the page many times, in this case we are calling the Go function again and again.

spinning.go:

package main
import (
    "fmt"
    "syscall/js"
)
const width = "width:200px;"
const height = "height:200px;"
const backgroundColor = "background-color:red;"
const center = `margin:auto;`
const font = "color:white;"
var element js.Value
var doc js.Value
var counter int
var beforeUnloadChannel = make(chan struct{})
func main() {

First we’ll just draw the initial red box.

    doc = js.Global().Get("document")
    element = doc.Call("getElementById", "spinning")
    element.Call("setAttribute", "style", width+height+backgroundColor+center+font)

Then, we’ll make a callback and invoke it when a certain javascript function is run.

    cb := js.NewCallback(spin)
    defer cb.Release()
    runSpin := js.Global().Get("runSpin")
    runSpin.Invoke(cb)
    <-beforeUnloadChannel
}

The actual function which is run.

func spin(args []js.Value) {
    element.Call("setAttribute", "style", rotate(counter*2)+width+height+backgroundColor+center+font)
    counter++
    fmt.Println("spin", counter)
}

Helper functions.

func rotate(degree int) string {
    return fmt.Sprintf(`-webkit-transition: -webkit-transform 1.5s linear;transform: rotate(%vdeg);`, degree)
}
func beforeUnload(event js.Value) {
    beforeUnloadChannel <- struct{}{}
}

If you again run:

cd spinning
bash build.sh && ./server

you should see a spinning red box (below just a not spinning image :) ).