useWasmWorker Hook
This hook provides a React-friendly interface to wasmWorker.js for calling functions exported by the Image WASM module.
It keeps all heavy computation off the main thread by delegating work to a Web Worker, while abstracting away memory management and WASM interop.
- Worker lifecycle and messaging
- WASM module initialization
- Memory allocation (
_malloc) and cleanup (_free) - TypedArray ↔ WASM heap copying
- WASM function invocation
- Return value and output buffer handling
Two Layers of the API
useWasmWorker exposes two levels of interaction:
- A generic
callmethod – low-level, flexible, mirrors raw WASM exports - Convenience wrapper functions – high-level, ergonomic, domain-specific helpers
Most application code should use the convenience wrappers. The generic call method exists for extensibility and advanced use cases.
Generic call Method
Signature
call({
funcName: string,
args?: Record<string, any>,
bufferKeys?: { key: string; type: keyof WASM_TYPES }[],
returnType?: 'void' | 'string' | 'Int32Array' | 'Uint8Array' | 'Uint8ClampedArray'
}) => Promise<{ output: Record<string, TypedArray>; returnValue: any }>
Also see the WASM_TYPES documentation to understand the supported bufferKeys types.
void is a supported returnType but not bufferKeys type.
Example
const { call } = useWasmWorker();
const { output } = await call({
funcName: "add_arrays",
args: {
a: arrayA,
b: arrayB,
out: outputArray,
length: arrayA.length,
},
bufferKeys: [
{ key: "a", type: "Int32Array" },
{ key: "b", type: "Int32Array" },
{ key: "out", type: "Int32Array" },
],
});
console.log(output.out);
Key Rules (Very Important)
The order of arguments passed to WASM must exactly match the C++ function signature.
Although args is an object, the worker calls the WASM function using:
wasmModule[`_${funcName}`](...Object.values(args));
That means:
int add(int a, int b);
Must be called as:
args = { a, b }; // correct
args = { b, a }; // ❌ wrong
Convenience Wrapper Functions
The hook exposes several predefined helpers that wrap call() with the correct argument order, buffer configuration, and defaults.
These functions:
- Hide
bufferKeysandreturnType - Enforce correct argument ordering
- Return clean JS values instead of raw WASM outputs
gaussianBlur
Calls the Gaussian blur from the Image WASM module.
Signature
gaussianBlur(params: {
pixels: Uint8ClampedArray;
width: number;
height: number;
/**
* @default width * 0.005
*/
sigma_pixels?: number;
}): Promise<Uint8ClampedArray>
Example Usage
const { gaussianBlur } = useWasmWorker();
const blurredPixels = await gaussianBlur({
pixels,
width,
height,
sigma_pixels: width * percentageOfWidth,
});
bilateralFilter
Calls the bilateral filter from the Image WASM module.
Signature
bilateralFilter(params: {
pixels: Uint8ClampedArray;
width: number;
height: number;
/**
* @default 3
*/
sigma_spatial?: number;
/**
* @default 50
*/
sigma_range?: number;
/**
* @default 0
*/
color_space?: number;
/**
* @default 8
*/
n_threads?: number;
}): Promise<Uint8ClampedArray>
color_space parameterPossible values:
- 0: CIE La*b* (closer to human perception)
- 1: sRGB (the default color space for computers)
The default for this parameter is CIE LAB because it is closer to human perception, and bilateral filters typically produce better edge-preserving results in perceptually uniform color spaces.
Example Usage
const { bilateralFilter } = useWasmWorker();
const filteredPixels = await bilateralFilter({
pixels,
width,
height,
sigma_spatial: 2,
sigma_range: 30,
color_space: 1,
});
blackThreshold
Calls the black threshold function from the Image WASM module.
Signature
blackThreshold(params: {
pixels: Uint8ClampedArray;
width: number;
height: number;
num_colors: number;
}): Promise<Uint8ClampedArray>
Example Usage
const { blackThreshold } = useWasmWorker();
const thresholdedPixels = await blackThreshold({
pixels,
width,
height,
num_colors: 8,
});
kmeans
Calls the K-Means clustering function from the Image WASM module.
Signature
kmeans(params: {
pixels: Uint8ClampedArray;
width: number;
height: number;
num_colors: number;
/**
* @default 100
*/
max_iter?: number;
}): Promise<{ pixels: Uint8ClampedArray; labels: Int32Array }>
Example Usage
const { kmeans } = useWasmWorker();
const { pixels: clusteredPixels, labels } = await kmeans({
pixels,
width,
height,
num_colors: 8,
});
findContours
Finds contours after K-Means clustering from the Image WASM module.
Signature
findContours(params: {
pixels: Uint8ClampedArray;
labels: Int32Array;
width: number;
height: number;
/**
* `@default` 100
*/
min_area?: number;
/**
* `@default` false
*/
draw_contour_borders?: boolean;
}): Promise<{ svg: string; visualization: Uint8ClampedArray }>
Example Usage
const { findContours } = useWasmWorker();
const {
svg, // pixels converted to SVG string
visualization, // pixels raster image with contours outlined
} = await findContours({
pixels,
labels,
width,
height,
min_area: 100,
draw_contour_borders: false,
});
TypedArray Handling
All TypedArrays passed to WASM must be declared in bufferKeys.
Each entry specifies:
{
key: string;
type: "Int32Array" | "Uint8Array" | "Uint8ClampedArray";
}
The worker will:
- Allocate memory with
_malloc - Copy the JS array into the correct HEAP view
- Pass the pointer to WASM
- Read the buffer back after execution
- Free the memory
Supported Types
The worker currently supports:
Int32Array→HEAP32Uint8Array→HEAPU8Uint8ClampedArray→HEAPU8string(UTF-8)void(only for returned values)
Adding New WASM Functions
-
Export the function from C++ using the Img2Num
EXPORTEDmacro (preferred):#include "exported.h"
EXPORTED void my_function(int* data, int length); -
Ensure argument order is final – JS must match it exactly.
-
Add a convenience wrapper in
useWasmWorker:myFunction: async ({ data, length }) => {
const result = await call({
funcName: "my_function",
args: { data, length },
bufferKeys: [{ key: "data", type: "Int32Array" }],
});
return result.output.data;
};
Adding New TypedArray Types
If you add a new TypedArray type (e.g. Float32Array):
- Export the corresponding HEAP view via
EXPORTED_RUNTIME_METHODSin the module'sCMakeLists.txt. - Add an entry to
WASM_TYPESinwasmWorker.js - Implement
allocandread
Failing to do this will cause memory corruption or crashes.
Diagram: React → Worker → WASM Flow
Summary
useWasmWorkerprovides safe, async WASM access from React- Argument order must exactly match C++ signatures
- TypedArrays require explicit declaration via
bufferKeys - Convenience wrappers are the preferred API
- Memory allocation and cleanup are fully automatic