This package enables WebAssembly for React Native powered by C++ TurboModules and Wasm3, a fast and universal WebAssembly runtime.
react-native-webassembly provides React Native applications with the capability to execute universal Wasm binaries with native speed.
βοΈ This project is still in active development. The following tasks are still remaining to be completed:
- Sanitize C++ memory management practices.
- Normalize execution and result handling of userland
exportfunctions.- Test framework implementation.
-
First, ensure your React Native application supports the New Architecture:
-
Install
react-native-webassembly:yarn add react-native-webassembly # React Native npx expo install react-native-webassembly # Expo
-
If you're using Expo, don't forget to run
npx expo prebuildafter installing.
The goal of react-native-webassembly is to export a browser-equivalent interface to the WebAssembly API.
To initialize a new WebAssembly module, we'll need to instantiate an module using a buffer populated with a .wasm binary:
import axios from 'axios';
import * as WebAssembly from 'react-native-webassembly';
import HelloWorld from './hello-world.wasm';
const module = await WebAssembly.instantiate<{
add: (a: number, b: number) => number;
}>(HelloWorld);Note
To import
.wasmfiles directly, you will need to update yourmetro.config.js.
Alternatively, in the snippet below, we show how to download and instantiate the reference Hello World example stored at a remote location:
import axios from 'axios';
import * as WebAssembly from 'react-native-webassembly';
const {
data: bufferSource,
} = await axios({
url: 'https://github.com/torch2424/wasm-by-example/raw/master/examples/hello-world/demo/assemblyscript/hello-world.wasm',
method: 'get',
responseType: 'arraybuffer',
});
const module = await WebAssembly.instantiate<{
add: (a: number, b: number) => number;
}>(bufferSource);You'll notice that in our call to instantiate, we can also pass typing information for the Exports of the module. In this case, the hello-world.wasm binary exports a function to add two numbers, add.
Once configured, we can execute the compiled wasm module from our JavaScript code, using the type-safe exported interface:
module.instance.exports.add(1, 2); // 3.It's also possible to declare an importObject to receive callbacks from the compiled module, which declares a list of callback function implementations which can be invoked by the WebAssembly runtime.
Warning
Some native modules require the presence of certain function implementations. Without specifying module-specific required dependencies, instantiation will fail.
For example, the Circom library converts arithmetic circuits used for generating, evaluating and verifying SNARKs are expressed as WASM modules which require the runtime to define an exceptionHandler function belonging to the namespace runtime.
It's simple to define an importObject:
const module = await WebAssembly.instantiate<{
getVersion: () => number;
getFieldNumLen32: () => number;
// ...
}>(bufferSource, {
// Declare custom memory implementation.
env: {
memory: new WebAssembly.Memory({ initial: 32767 }),
},
// Define the scope of the import functions.
runtime: {
exceptionHandler: (value: number) => console.error(value),
},
});Here, we declare an exceptionHandler as runtime imports to the compiled module. Without declaring this required dependency, the module would fail to compile.
You can find a working implementation of this process in the Example App.
Currently, wasm3 only supports a single memory region. This means that WebAssembly files which contain multiple memory allocations are not currently supported.
react-native-webassembly exposes access to the runtime memory element for allocated instances, which is represented using an ArrayBuffer named memory. This shares the same backing array as the native runtime.
It can accessed as follows:
const module = WebAssembly.instantiate(...);
const memory: ArrayBuffer | undefined = module.instance.exports.memory;