WebAssembly -Part II.b | Golang with WASM

Prabakar Veer
tech-lah
Published in
4 min readMay 5, 2019

The Introduction Before the Introduction

Last time, we started off by explaining the core concepts of WebAssembly and how you can try it for yourself with little to no setup. If you need a refresher, please refer to this link.

Introduction

As WebAssembly offers another way of writing client-side logic with a low-level language for Web applications, Go + WebAssembly yields an amazing startup.

Here we are going to explore writing and compiling Go to WebAssembly with a simple calculator example from installation of Go, to compilation of WebAssembly, to the communication between Go and JavaScript.

Why Go ?

  • Static Typing: Its always good to use a language that supports type safety out of the box and gives us the ability to write our front-end apps.
  • Performance: Go with Goroutines is multi-threaded which is good for compute-heavy operations when JavaScript code runs in a single-thread.
  • Built in Web support: Go language creators have built in so many tools natively supported by language core.
  • By Google: This is not the only reason but Google created lots of free official guides and made Golang as open-source with huge collection of add-ons.

Let’s get started

To communicate between Go and JavaScript, we have to include a JavaScript based run-time. The Go 1.11.1 release ships with an implementation that allow us to invoke JavaScript APIs.

Set-up your Environment

  1. Install go from “ https://golang.org/doc/install” (I have chosen version 1.11.1)
  2. (optional) Test your “hello world” program using https://github.com/golang/go/wiki/WebAssembly

Step 1: Create you .go file for basic calculator

  1. Create a main.go file with syscall/js
import (
"syscall/js"
)

2. Create your calculator functions (add, sub, mul and div)

func registerCallbacks() {
js.Global().Set("add", js.NewCallback(add))
js.Global().Set("sub", js.NewCallback(sub))
js.Global().Set("mul", js.NewCallback(mul))
js.Global().Set("div", js.NewCallback(div))
}
func add(i []js.Value) {
result := js.ValueOf(i[0].Int() + i[1].Int())
setResult(result)
}
func sub(i []js.Value) {
result := js.ValueOf(i[0].Int() - i[1].Int())
setResult(result)
}
func mul(i []js.Value) {
result := js.ValueOf(i[0].Int() * i[1].Int())
setResult(result)
}
func div(i []js.Value) {
result := js.ValueOf(i[0].Int() / i[1].Int())
setResult(result)
}
// set the value to js -> output
func setResult(val js.Value) {
js.Global().Get("document")
.Call("getElementById", "output").Set("value", val)
}

3. Register those functions in js.Global with callback names which will be referred in HTML later.

func registerCallbacks() {
js.Global().Set("add", js.NewCallback(add))
js.Global().Set("sub", js.NewCallback(sub))
js.Global().Set("mul", js.NewCallback(mul))
js.Global().Set("div", js.NewCallback(div))
}

4. The main function

func main() {
c := make(chan struct{}, 0)
println("WASM Go Initialized")
registerCallbacks()
<-c
}

5. Build WebAssembly from your Go file

If you are using windows, ensure setting the Environment variables of GOARCH=wasm & GOOS=js (or) use bash

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

Step 2: Let’s build the HTML & JavaScript using simple Webpack

  1. Setup your code base using Webpack starter boilerplate https://github.com/wbkd/webpack-starter
  2. Copy wasm_exec.js file from “[$GOROOT]/misc/wasm/wasm_exec.js” to your ./Public folder. (Example: C:\Go\misc\wasm\wasm_exec.js)

3. Move the main.wasm file under ./public folder

4. Create the index.js file under ./Src/Scripts folder

import '../styles/index.scss';const go = new Go();let mod, inst;WebAssembly.instantiateStreaming(fetch("./public/main.wasm"), go.importObject).then(
result => {
mod = result.module;
inst = result.instance;
go.run(inst);
}
);
// global calc function
window.calc = function(op) {
let v1 = Number(document.getElementById("val1").value);
let v2 = Number(document.getElementById("val2").value);
if (!v1 || !v2) {
return;
}
if (op === '+') {
add(v1, v2);
} else if (op === '-') {
sub(v1, v2);
} else if (op === '*') {
mul(v1, v2);
} else if (op === '/') {
div(v1, v2);
}
}

5. Create the index.html file under ./Src folder

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Go as WASM - Simple Calculator</title>
<script src="public/wasm_exec.js"></script>
<script src="src/scripts/index.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
<div class="navbar">
<div class="card">
<div class="card-header">
Go as WASM - Simple Calculator
</div>
<div class="card-body">
<input type="text" id="val1" value="10" />
<input type="text" id="val2" value="5" />
= <input type="text" id="output" disabled />
<br />
<br />
<div class="button-group">
<button onClick="calc('+');">+</button>
<button onClick="calc('-');">-</button>
<button onClick="calc('*');">*</button>
<button onClick="calc('/');">/</button>
</div>
</div>
</div>
</div>
</body>
</html>

6. Let’s run it!

npm install
npm start

7. Cool! Now we have a nice and simple calculator using Go & WebAssembly.

You can find the source code from the below GitHub repository.

https://github.com/veerprabakar/goasweb

Further reading

“WebAssembly -Part II.A | Wasm with Rust” by Francisco Vilches

https://link.medium.com/Hutwlz34rW

--

--