WebAssembly with Go

back

WebAssembly (often abbreviated as Wasm) is a binary instruction format for a stack-based virtual machine. 1

WebAssembly code is compiled into a binary format executed directly by the browser’s virtual machine rather than interpreted like traditional JavaScript code.

It runs in a sandboxed environment, preventing it from accessing sensitive user data or interfering with other web page parts.

WebAssembly is also designed to be portable, meaning that it can be used across a wide range of platforms and devices. This makes it an ideal tool for building web applications that need to work seamlessly across different devices and operating systems.

Write WebAssembly With Go

WebAssembly is a binary format that allows for code deployment on the web. It is designed to be fast, efficient, and secure and can be used with various programming languages, including Go.

To use WebAssembly with Go, you must compile your Go code to WebAssembly. After compiling the code, it can be used just like any other JavaScript code in the web application.

go mod init wasmcounter
// main.go
package main

import "fmt"

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

Compile our code into wasm format. Use two flags GOOS=js and GOARCH=wasm for the Go compiler.

GOOS=js GOARCH=wasm go build -o main.wasm main.go

# copy the `wasm_exec.js` with this command:
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
<!-- index.html -->
<html>
<head>
  <meta charset="utf-8" />
  <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>
  <h1>Hello WebAssembly</h1>
</body>
</html>
# to serve the page
python3 -m http.server 6161
# open the page
open http://localhost:6161

image

A Counter App

// main.go
package main

import (
  "fmt"
  "syscall/js"
)

func main() {
  c := make(chan bool)
  fmt.Println("Hello from Go WebAssembly!")
  js.Global().Set("increment", js.FuncOf(increment))
  js.Global().Set("decrement", js.FuncOf(decrement))
  <-c
}

var count int

func increment(this js.Value, args []js.Value) interface{} {
  count++
  return count
}

func decrement(this js.Value, args []js.Value) interface{} {
  count--
  return count
}
GOOS=js GOARCH=wasm go build -o main.wasm main.go
# copy the `wasm_exec.js` with this command:
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
<!-- index.html -->
<html>
<head>
  <meta charset="utf-8" />
  <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>
  <h1>WebAssembly in Go</h1>
  <button id="increment" onclick="incrementCounter()">Increment</button>
  <span id="count">0</span>
  <button id="decrement" onclick="decrementCounter()">Decrement</button>
</body>
<script>
  const incrementCounter = () => {
    const count = increment()
    setCounterValue(count)
  }
  const decrementCounter = () => {
    const count = decrement()
    setCounterValue(count)
  }
  const setCounterValue = (value) => {
    const countEl = document.getElementById("count")
    countEl.textContent = value
  }
</script>
</html>
# to serve the page
python3 -m http.server 6161
# open the page
open http://localhost:6161

image