Packaging Uno into the browser using WebAssembly

Back in January, we made a little CLI tool called uno available, that my colleague @alexandrepesant built. It takes log data and only shows what is a “unique” log line. It is a barebones tool showcasing one of the core parts of our product, anomaly detection, but using a simpler algorithm based on edit distance.

As we saw quite a big interest when launching it, we wanted to see if we could make it available without the need to download a binary as well as giving it a proper home.

The first use case we wanted to tackle is uploading a text-based log file, and seeing the output directly in the browser.

We considered setting up an endpoint to which you could send or upload logs, but this would mean your log data would need to leave your computer. 

Instead, the simplest and most straightforward path seemed to be to try to package it into the browser using WebAssembly.

WebAssembly is a low-level compilation target for many languages like Go, C and C++. It is since 2017 supported in all major browsers and enables performance critical code to run in the client or other JavaScript environments.

This means we could take our existing Go code, compile it to a WebAssembly module:

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

To be able to use the compiled main.wasm in the client, we first need to setup Go’s runtime on the browser. The Go authors have written a wasm_exec.js file that does exactly this and provides a global Go constructor.

By including this file into our project, we can then use that global Go constructor to create a go object. and use that together with the compiled webassembly module main.wasm, to instantiate it in the componentDidMount() lifecycle hook in React, using WebAssembly.instantiateStreaming():

componentDidMount() {
const go = new Go();

WebAssembly.instantiateStreaming(
fetch("main.wasm"),
go.importObject
).then(async result => {
go.run(result.instance);
});
}

 And we can now call our runUno function, from the Go code, directly in React:

const output = runUno(input)

Try it out at https://feeduno.io 🙂

Written by Gustav Larsson
on July 8, 2019