In this quick guide I’ll show you how you can run Go through WASM in a browser extension using the WXT template.
First install Go, tinygo and binaryen.
scoop install go
scoop install tinygo
scoop install binaryen
Then install pnpm and bootstrap WXT with your favourite frontend framework. I will be using Vue.
pnpm dlx wxt@latest init <project-name>
Create directory for the Go code and make a Go source file
mkdir wasm_code
cd wasm_code
touch add.go
Paste the following in add.go
package main
// The line below is very important
//export Add
func Add(a int, b int) int {
return a + b
}
func main() {
}
Compile your Go code
tinygo build -o ../assets/add.wasm -target=wasm ./add.go
Open components/HelloWorld.vue
and edit it as follows
<script lang="ts" setup>
import { ref } from "vue";
import addModule from "@/assets/add.wasm?url";
defineProps({
msg: String,
});
const wasmInstance = ref<WasmInstance | null>(null);
const wasmResult = ref<number>(0);
const a = ref(2);
const b = ref(5);
interface WasmExports {
Add: (a: number, b: number) => number;
// Add other exports here
}
interface WasmInstance {
exports: WasmExports;
}
const loadWasmModule = async () => {
try {
const importObject = {
gojs: {
Add: (a: number, b: number) => {},
},
wasi_snapshot_preview1: { fd_write: () => {} }, // Needed for instantiateStreaming, otherwise throws errors TypeError and LinkError
};
const fetchedModule = await fetch(addModule);
const result = await WebAssembly.instantiateStreaming(
fetchedModule,
importObject
);
// console.log(result.instance.exports);
wasmInstance.value = result.instance as unknown as WasmInstance;
} catch (error) {
console.error("Failed to load WebAssembly module:", error);
}
};
const addWasm = () => {
if (wasmInstance.value) {
const result = wasmInstance.value.exports.Add(a.value, b.value);
wasmResult.value = result;
} else {
console.error("WebAssembly module not loaded.");
}
};
loadWasmModule();
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<div>click below to sum {{ a }} and {{ b }}</div>
<button type="button" @click="addWasm">result is {{ wasmResult }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Install
<a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
in your IDE for a better DX
</p>
<p class="read-the-docs">Click on the WXT and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>
Make sure you have content_security_policy
set in your wxt config.
For Chrome
import { defineConfig } from "wxt";
// See https://wxt.dev/api/config.html
export default defineConfig({
modules: ["@wxt-dev/module-vue"],
manifest: {
content_security_policy: {
extension_pages:
"script-src 'self' 'wasm-unsafe-eval'; object-src 'self'",
},
},
});
For Firefox it should be
import { defineConfig } from "wxt";
// See https://wxt.dev/api/config.html
export default defineConfig({
modules: ["@wxt-dev/module-vue"],
manifest: {
content_security_policy: "script-src 'self' 'wasm-eval'; object-src 'self'",
},
});
PNPM install and run your extension
pnpm install
Then
pnpm dev
or
pnpm dev:firefox
Congratulations!🎉 You are now ready to experiment with WASM and web extensions.
Additionally, check out the WASM tinygo guide to see how to import functions from Javascript to Go.
Full source code can be found at Github.