WebAssembly in 5 Minutes

The Go Wiki has the (maybe) most minimal code to demonstrate running Go in the browser with WebAssembly. It prints a message to the browser console.

But how can Go interact with the browser? Let's create a simple web app to explore reading from, and writing to, the browser's domain object model (DOM). Based on the code in the Go Wiki, we just need these additional ingredients:

  1. An <input> field to enter text and a <div> as output target on the HTML page
  2. Go code that reads the <input> field, converts the text to uppercase, and writes the result to the output div.

The final result will look like this:

I'll list all steps here so that they're easy to follow, even though this means duplicating some of the code from the Go Wiki.

0. Set up a new project

Create a directory of your choice, cd into it, and initialize a new Go project:

go mod init <a module path of your choice>

1. Get the JavaScript support file

The Go code needs a JS support file for accessing the browser DOM. It must match the version of the Go toolchain, so ensure to fetch it from the current Go installation:

cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" .

2. Add input and output to the HTML page

Start with the HTML file from the Go Wiki. It contains a script that loads the JS support file, and another script that loads and calls the WebAssembly module (WASM) that you'll build in step 4.

You only need to add the input field and the output <div> to the HTML body:

<!DOCTYPE html>
<html>

<!-- The head from the Go Wiki -->
<head>
	<meta charset="utf-8" />
	<script src="wasm_exec.js"></script>
	<script defer>
		const go = new Go();
		WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
			go.run(result.instance);
		});
	</script>
</head>

<!-- Add just this: -->
<body>
	<input type="text" id="input" placeholder="Type here...">
	<div id="output"></div>
</body>

</html>

You might also want to add a defer to the second script tag as shown above, to ensure the script loads late enough to find the WebAssembly module.

Copy this HTML page into a file named “index.html”.

3. Write Go code to read, convert, and write the input

In this step, you'll create Go code to –

  • define a function that acts as an event listener for the input field,
  • make this function read from the input field, convert the field value to uppercase, and write the result to the output div,
  • and register this function as an event handler to the input field.

For easy copying and pasting, I do not split the code into many small snippets and comment on each; rather, find relevant comments inside the code that will explain what's going on (and no, I don't intend to promote the bad practice of commenting what code does. These comments are for learning purposes and mostly shouldn't exist in non-educational code):

package main

import (
	"strings"
	"syscall/js"
)

// This function returns a js.Func that acts as an event listener for the input field. It receives the input field via the "this" parameter.
func uppercaseFunction() js.Func {
	return js.FuncOf(func(this js.Value, args []js.Value) any {
		// Get the input field's value
		inputText := this.Get("value").String()

		// Process the value
		uppercaseText := strings.ToUpper(inputText)

		// Get the DOM document object
		document := js.Global().Get("document")

		// Find the output element and write the uppercase text to it
		outputElement := document.Call("getElementById", "output")
		outputElement.Set("textContent", uppercaseText)

		return nil
	})
}

func main() {
	// Add the uppercase function as an event listener to the input field
  // This line saves you from having to write JavaScript for adding the event handler
	js.Global().Get("document").Call("getElementById", "input").Call("addEventListener", "input", uppercaseFunction())

	// Keep the WebAssembly program running indefinitely
	// Without this, the program would exit and JavaScript couldn't call our function
	select {}
}

Create a new file main.go and copy the above code into it.

4. Compile the Go code to a WebAssembly module

Inside the HTML document, the initialization script wants to fetch a WebAssembly module named main.wasm, which does not exist yet.

Create this file from the Go code as follows:

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

The OS and ARCH targets tell Go to build a WASM for use in the browser (with JavaScript). The -o flag ensures the compiled WASM has the name the initialization script expects.

For more examples on interacting with the DOM, see the section Interacting with the DOM in the Go Wiki.

5. Serve the page

At this point, your project directory should contain these files:

index.html
main.go
main.wasm
wasm_exec.js
go.mod

Run a Web server of your choice to serve index.html. If you have Caddy installed, this step is straightforward:

caddy file-server -l localhost:8080

Python is another option:

python3 -m http.server 8080

Then, open the page at localhost:8080 and type something into the input field.

Does it work? Wonderful! Take this as a starting point for some great WebAssembly project!