Export WebAssembly Functions to JavaScript

To run high-performance WebAssembly (Wasm) code in a web application, you must export functions from your compiled binary so JavaScript can access them. This article explains how to mark functions for export in languages like C/C++ and Rust, compile them into a .wasm module, and load and call those functions directly within your JavaScript runtime.

Step 1: Export the Function in Your Source Language

Before compiling your code to WebAssembly, you must explicitly declare which functions should be exposed to the outside environment.

In C/C++ (using Emscripten)

Use the EMSCRIPTEN_KEEPALIVE attribute. This prevents the compiler from optimizing the function away and automatically adds it to the exported functions list.

#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
    return a + b;
}

In Rust

Use the #[no_mangle] attribute and the pub extern "C" signature to ensure the function name remains readable and uses the standard C calling convention.

#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
    a + b
}

In WebAssembly Text Format (WAT)

If writing raw WebAssembly Text, use the export keyword inside your module definition.

(module
  (func $add (param $a i32) (param $b i32) (result i32)
    local.get $a
    local.get $b
    i32.add)
  (export "add" (func $add))
)

Step 2: Load and Call the Function in JavaScript

Once you compile your source code into a .wasm file, you must load, compile, and instantiate it in JavaScript. The exported functions are exposed on the instance.exports object of the instantiated WebAssembly module.

The most efficient way to load WebAssembly in the browser is via WebAssembly.instantiateStreaming, which compiles the module while it is still downloading.

async function initWasm() {
  try {
    // Fetch and compile the WebAssembly binary
    const { instance } = await WebAssembly.instantiateStreaming(
      fetch('module.wasm')
    );

    // Access the exported "add" function
    const addResult = instance.exports.add(15, 30);
    
    console.log(`Result from Wasm: ${addResult}`); // Outputs: Result from Wasm: 45
  } catch (error) {
    console.error("Failed to load WebAssembly module:", error);
  }
}

initWasm();

Fallback for Older Browsers or Node.js

If instantiateStreaming is not supported (for example, in older Node.js environments), you can read the binary into an ArrayBuffer first and then compile it:

const fs = require('fs');

async function initNodeWasm() {
  const wasmBuffer = fs.readFileSync('module.wasm');
  const { instance } = await WebAssembly.instantiate(wasmBuffer);
  
  const result = instance.exports.add(10, 20);
  console.log(result); // Outputs: 30
}

initNodeWasm();