Loading and running WebAssembly code
To use WebAssembly in JavaScript, you first need to pull your module into memory before compilation/instantiation. This article provides a reference for the different mechanisms that can be used to fetch WebAssembly bytecode, as well as how to compile/instantiate then run it.
What are the options?
WebAssembly is not yet integrated with <script type='module'>
or import
statements, thus there is not a path to have the browser fetch modules for you using imports.
The older WebAssembly.compile
/WebAssembly.instantiate
methods require you to create an ArrayBuffer
containing your WebAssembly module binary after fetching the raw bytes, and then compile/instantiate it. This is analogous to new Function(string)
, except that we are substituting a string of characters (JavaScript source code) with an array buffer of bytes (WebAssembly source code).
The newer WebAssembly.compileStreaming
/WebAssembly.instantiateStreaming
methods are a lot more efficient — they perform their actions directly on the raw stream of bytes coming from the network, cutting out the need for the ArrayBuffer
step.
So how do we get those bytes into an array buffer and compiled? The following sections explain.
Using Fetch
Fetch is a convenient, modern API for fetching network resources.
The quickest, most efficient way to fetch a Wasm module is using the newer WebAssembly.instantiateStreaming()
method, which can take a fetch()
call as its first argument, and will handle fetching, compiling, and instantiating the module in one step, accessing the raw byte code as it streams from the server:
WebAssembly.instantiateStreaming(fetch("simple.wasm"), importObject).then(
(results) => {
// Do something with the results!
},
);
If we used the older WebAssembly.instantiate()
method, which doesn't work on the direct stream, we'd need an extra step of converting the fetched byte code to an ArrayBuffer
, like so:
fetch("module.wasm")
.then((response) => response.arrayBuffer())
.then((bytes) => WebAssembly.instantiate(bytes, importObject))
.then((results) => {
// Do something with the results!
});
Aside on instantiate() overloads
The WebAssembly.instantiate()
function has two overload forms — the one shown above takes the byte code to compile as an argument and returns a Promise that resolves to an object containing both the compiled module object and an instantiated instance of it. The object looks like this:
{
module: Module, // The newly compiled WebAssembly.Module object,
instance: Instance, // A new WebAssembly.Instance of the module object
}
Note: Usually we only care about the instance, but it's useful to have the module in case we want to cache it, share it with another worker or window via postMessage()
, or create more instances.
Note: The second overload form takes a WebAssembly.Module
object as an argument, and returns a promise directly containing the instance object as the result. See the Second overload example.
Running your WebAssembly code
Once you've got your WebAssembly instance available in your JavaScript, you can then start using features of it that have been exported via the WebAssembly.Instance.exports
property. Your code might look something like this:
WebAssembly.instantiateStreaming(fetch("myModule.wasm"), importObject).then(
(obj) => {
// Call an exported function:
obj.instance.exports.exported_func();
// or access the buffer contents of an exported memory:
const dv = new DataView(obj.instance.exports.memory.buffer);
// or access the elements of an exported table:
const table = obj.instance.exports.table;
console.log(table.get(0)());
},
);
Note: For more information on how exporting from a WebAssembly module works, have a read of Using the WebAssembly JavaScript API, and Understanding WebAssembly text format.
Using XMLHttpRequest
XMLHttpRequest
is somewhat older than Fetch, but can still be happily used to get a typed array. Again, assuming our module is called simple.wasm
:
- Create a new
XMLHttpRequest()
instance, and use itsopen()
method to open a request, setting the request method toGET
, and declaring the path to the file we want to fetch. - The key part of this is to set the response type to
'arraybuffer'
using theresponseType
property. - Next, send the request using
XMLHttpRequest.send()
. - We then use the
load
event handler to invoke a function when the response has finished downloading — in this function we get the array buffer from theresponse
property, and then feed that into ourWebAssembly.instantiate()
method as we did with Fetch.
The final code looks like this:
const request = new XMLHttpRequest();
request.open("GET", "simple.wasm");
request.responseType = "arraybuffer";
request.send();
request.onload = () => {
const bytes = request.response;
WebAssembly.instantiate(bytes, importObject).then((results) => {
results.instance.exports.exported_func();
});
};
Note: You can see an example of this in action in xhr-wasm.html.