C/C++ code in React using WebAssembly
Tags: javascript, cpp, webassembly, beginners
Published: 2023-09-13 04:54:14

I started looking into WebAssembly (WASM) recently having a small project in mind. The project is to make a web interface for an open-source numeric C++17 library. I was thinking about a web application using a popular framework such as React. So here we go. This post is about my first experience with using C code compiled to WebAssembly in React.

While it was relatively easy to compile and call C-code from JavaScript which I described in the [first](https://dev.to/iprosk/my-first-take-on-webassembly-213h) and [second](https://dev.to/iprosk/playing-with-low-level-memory-in-webassembly-257i) posts, making it work with React required much more time and patience. I did my homework, and I knew about difficulty with loading WASM modules. I read about solutions based on `wasm-loader` with`react-app-rewired` as discussed [here](https://www.telerik.com/blogs/using-webassembly-with-react).  And I tried `fetch` WASM directly as described in [MDN Docs](https://developer.mozilla.org/en-US/docs/WebAssembly/Loading_and_running). But each time the result was the same -- my WASM module refused to load at runtime complaining about "incorrect MIME types" or something like that.

Finally, the only solution that really worked for me was to compile C code in [Emscripten](https://emscripten.org/) with `-sSINGLE_FILE=1`. What it does? It embeds WebAssembly binary directly into JavaScript glue code, which is then used to call byte code at runtime.  This solved my problems with loading WASM modules.

Here is what I did step by step...

# Setting up a React App

If you are new to React (like myself), there is a good introduction at [MDN Docs](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started). I used Node.js v16.20.0, which is bundled with Emscripten compiler. Just type in the terminal (I used PowerShell this time)
```
$npx create-react-app  react-wasm
$cd react-wasm
$npm start
```
And we have a demo React project in the directory `react-wasm` up and running at `localhost:3000`. We will need to install some more stuff for our demo, but it can be done later. There is no rush...

# Preparing a toy C library

I am going a prepare a toy C library. One function will be only for side effects, and another one will be a "numeric algorithm" -- it will take data from an input buffer, transform it, and put it into an output buffer. The input buffer will be filled in from JavaScript and the output data should be rendered by some React component (I will just use `react-plotly.js` to make some visuals). So here is our C file:

```
// hello_react.c

#include <assert.h>
#include <stdio.h>

void hello_react() {
    printf("Hello, React!\n");
}

void process_data(double* input, double* output, int size) {
    int i;
	
    assert(size > 0 && "size must be positive");
    assert(input && output && "must be valid pointers");
	
    for (i = 0; i < size; i++) {
        output[i] = input[i] * input[i];
    }
}
```
If you like to compile this file as C++, just wrap the function into `extern "C" {}` to prevent function names being [mangled](https://en.wikipedia.org/wiki/Name_mangling) (in a WASM binary our functions will be translated into symbols `_hello_react` and `_process_data`). I saved this file as `react-wasm/src/hello_react.c` into `src` folder of the React project.

The next step is to compile this file into WASM binary and JavaScript glue code to interface it with React. I use [Emscripten](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html) compiler that wraps around LLVM. There are two compiler options that make our life easier. One is `-sMODULARIZE` that helps to run WASM code in Node.js. Another one is `-sSINGLE_FILE=1`.

As I mentioned above, without `-sSINGLE_FILE=1` the `emcc` compiler produces a separate `*.wasm` file that has to be loaded into browser at runtime. In my case (I mostly followed a method described [here](https://www.telerik.com/blogs/using-webassembly-with-react) with some variations), whenever I used a separate file, it refused to load, despite the fact that I could build a package with `npm`. According to [Emscripten FAQ](https://emscripten.org/docs/getting_started/FAQ.html?highlight=single_file): "another option than a local webserver is to bundle everything into a single file, using -sSINGLE_FILE (as then no XHRs will be made to file:// URLs)." This worked for me.

Okay, the final command to compile out toy C "library" with Emscripten looks like this.
```
$emcc hello_react.c -o hello_react.js -sMODULARIZE -sSINGLE_FILE=1 -sEXPORTED_FUNCTIONS=_hello_react,_process_data,_malloc,_free,getValue -sEXPORTED_RUNTIME_METHODS=ccall
```
Now, we have `hello_react.js` that contains JavaScript glue code and embedded WASM code (and we *don't have* a separate `hello_react.wasm`). We also asked the compiler to export some functions for us including `_hello_react` and `_process_data`.

# Interfacing with React components

Now comes the fun stuff. How can we integrate our compiled file `hello_react.js` into a React component? Let's move step by step...

To call a JavaScript module with WASM code, I will mostly follow the method for interoperability with Node.js from [Emscripten Docs](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#interacting-with-code). The basics idea is to to call `require('./hello_wasm.js')` which returns a promise.

```
var factory = require('./hello_react.js');

factory().then((instance) => {
  instance._hello_react(); // direct calling
  instance.ccall("hello_react", null, null, null);
  // more code...
});
```
Here, we can call our C function directly via the exported symbol `_hello_react` or using a runtime method `ccall`.

Now `index.js` inside my `src` folder looks like this.

```
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

var factory = require('./hello_react.js');

factory().then((instance) => {
  instance._hello_react(); // direct calling
  instance.ccall("hello_react", null, null, null);
});

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
```
At this point, we can run our project with `npm start`... And see that it doesn't work.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xelqjtsa1vt78ct8y4pi.png)

The first problem is specific to webpack, and there is a [standard workaround](https://stackoverflow.com/questions/70591567/module-not-found-error-cant-resolve-fs-in-react), which is called `react-app-rewired`. So our next step is to install this package.

```
$npm install react-app-rewired
```
And set up `config-overrides.js` in the root directory of the project with the following content.

```
module.exports = function override(config, env) {
  config.resolve.fallback = {
    fs: false
  };
  return config;
};
```
After that I just modify the `script` section of the `package.json` to call `react-app-rewired` at startup.
```
"scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
  },
```
Next step is `npm install path` so we can silence the following.
```
Module not found: Error: Can't resolve 'path' in 'C:\Users\Igor\Devel\react-wasm\src'
```
This almost works. The only remaining thing is the annoying `eslint` complaints, which I silenced using a temporary solution simply by adding the following lines on top of `hello_react.js`.

```
/* eslint-disable no-undef */
/* eslint-disable  no-restricted-globals */
/* eslint-disable import/no-amd */
```
Now, we open the console, and see that our function has been called.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4cvxpqex5i51uyzis3ey.png)

Okay, this part works. We can even built package with `npm run build` at this point. Now let's go a little bit further and try to connect WASM code with a React component.

# Mocking interface between C code and a React component

I will use [Plotly.js](https://plotly.com/javascript/react/) as a simple graphical react component. 
```
$npm install plotly-react.js plotly.js
```

Since I am using low-level C code here, I will need to make an input buffer using `_malloc` to pass data to the WASM module. We already exported it when compiled our C file with `emcc`. And I will use a separate output buffer to get data out. Let me skip some little steps here. More details can be found in my [previous post](https://dev.to/iprosk/playing-with-low-level-memory-in-webassembly-257i).

Out `index.js` now looks like this.

```
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

var factory = require('./hello_react.js');

factory().then((instance) => {
  const inputArr = new Float64Array([0, 1, 2, 3, 5]);
  const inputBuff = instance._malloc(inputArr.length * inputArr.BYTES_PER_ELEMENT);
  instance.HEAPF64.set(inputArr, inputBuff / inputArr.BYTES_PER_ELEMENT);

  let outputArr = new Float64Array(inputArr.length);
  const outputBuff = instance._malloc(inputArr.length * inputArr.BYTES_PER_ELEMENT);
  
  instance.ccall('process_data', 'number', ['number', 'number', 'number'], [inputBuff, outputBuff, inputArr.length]);

  for (let i = 0; i < outputArr.length; i++) {
      outputArr[i] = instance.getValue(outputBuff + i * outputArr.BYTES_PER_ELEMENT, 'double');
  }

  console.log(inputArr);
  console.log(outputArr);
  
  instance._free(outputBuff);
  instance._free(inputBuff);
  
  const root = ReactDOM.createRoot(document.getElementById('root'));

  root.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );

});
```
To get data out of the output buffer, I used a low-level operation `getValue` (there is no `HEAPF64.get` in the API).

```
for (let i = 0; i < outputArr.length; i++) {
      outputArr[i] = instance.getValue(outputBuff + i * outputArr.BYTES_PER_ELEMENT, 'double');
  }
```
Let's check console logs and find correct input and output array values.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5w0018fez2emnqt8ii3l.png)

Let's now use this data somewhere...

# Let's visualize what we get

We can add a simple script into `src` folder to plot input and output using a Plotly.js component `<Plot />` (sprinkle it with some CSS if you like).

```
// MyPlot.js
import React from 'react';
import Plot from 'react-plotly.js';
import './MyPlot.css'

function MyPlot(props) {

    const xs = props.xs;
    const ys = props.ys;

    const plots = [{
        x: xs, 
        y: ys, 
        type: 'scatter', 
        mode: 'lines+markers', 
        marker: {color: 'red'}
    }];

    return (
        <div className='MyPlot'>
            <Plot
              data={ plots }
              layout={ {width: 640, height: 480, title: 'Plotly React'} }
            />
        </div>
    );
}

export default MyPlot;
```
Now the component is ready to use from `index.js`. Just add `import MyPlot from './MyPlot'` and update the rendering

```
root.render(
    <React.StrictMode>
	  <App />
	  <MyPlot xs={inputArr} ys={outputArr} />
    </React.StrictMode>
  );
```
Voila!

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/loi49j6nc7019c1n5grb.png)