Skip to content
Branch: master
Find file History
johanbrandhorst and deadprogram src/examples/wasm: Show both methods supported
Adds another example showing the simple case
of executing main, adds a README explaining how
everything fits together and how to execute the compiled
code in the browser. Include a minimal webserver for
local testing.
Latest commit 586023b Apr 16, 2019

README.md

TinyGo WebAssembly examples

The examples here show two different ways of using WebAssembly with TinyGo;

  1. Defining and exporting functions via the //go:export <name> directive. See the export folder for an example of this.
  2. Defining and executing a func main(). This is similar to how the Go standard library implementation works. See the main folder for an example of this.

Building

Build using the tinygo compiler:

$ tinygo build -o ./wasm.wasm -target wasm ./main/main.go

This creates a wasm.wasm file, which we can load in JavaScript and execute in a browser.

This examples folder contains two examples that can be built using make:

$ make export
$ make main

Running

Start the local webserver:

$ go run main.go
Serving ./html on http://localhost:8080

fmt.Println prints to the browser console.

How it works

Execution of the contents require a few JS helper functions which are called from WebAssembly. We have defined these in wasm_exec.js. It is based on $GOROOT/misc/wasm/wasm_exec.js from the standard library, but is slightly different. Ensure you are using the same version of wasm_exec.js as the version of tinygo you are using to compile.

The general steps required to run the WebAssembly file in the browser includes loading it into JavaScript with WebAssembly.instantiateStreaming, or WebAssembly.instantiate in some browsers:

const go = new Go(); // Defined in wasm_exec.js
const WASM_URL = 'wasm.wasm';

var wasm;

if ('instantiateStreaming' in WebAssembly) {
	WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
		wasm = obj.instance;
		go.run(wasm);
	})
} else {
	fetch(WASM_URL).then(resp =>
		resp.arrayBuffer()
	).then(bytes =>
		WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
			wasm = obj.instance;
			go.run(wasm);
		})
	)
}

If you have used explicit exports, you can call them by invoking them under the wasm.exports namespace. See the export directory for an example of this.

In addition to this piece of JavaScript, it is important that the file is served with the correct Content-Type header set.

package main

import (
	"log"
	"net/http"
	"strings"
)

const dir = "./html"

func main() {
	fs := http.FileServer(http.Dir(dir))
	log.Print("Serving " + dir + " on http://localhost:8080")
	http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
		if strings.HasSuffix(req.URL.Path, ".wasm") {
			resp.Header().Set("content-type", "application/wasm")
		}

		fs.ServeHTTP(resp, req)
	}))
}

This simple server serves anything inside the ./html directory on port 8080, setting any *.wasm files Content-Type header appropriately.

You can’t perform that action at this time.