How to bundle Wasm in npm package

dannadori
3 min readNov 24, 2021

Note:
This article is also available here.(Japanese)
https://zenn.dev/wok/articles/0021_bundle-tensorflowjs-model

Introduction

I’ve been creating npm packages to run various machine learning models with WebWorker. Some of them use Tensorflow Lite (TFLite) models. Tensorflow team is developping Tensorflowjs to run TFLite models but this is alpha version yet. So by for now, you need to compile TFLite into WebAssembly(wasm) format by yourself. (TFLite wasm conversion.) There are also examples of wasm for example OpenCV, ffmpeg or so.

When providing npm packages that include such a wasm file, it is a little troublesome to ask users to do additional work such as copying the wasm file after npm install. In previous articles, I have introduced methods to bundle WebWorker and tensorflowjs models with the same awareness of the issue. In this article, I would like to introduce a method to bundle wasm files.

Premise

I’m assuming webpack 5 is used, I haven’t tried it with webpack 4.

Creating npm package

First, place the wasm file in the root of the npm package as a preparation. In this case, we will place it under a folder called resources. In the example below, we have also created a simd version.

$ ls resources/
custom_opencv-simd.js custom_opencv-simd.wasm custom_opencv.js custom_opencv.wasm

The next step is to set the rules for bundling these model files in webpack.config.js. With webpack5, url-loader and file-loader are no longer needed, and the configuration has been simplified.

It is better to use asset/inline for wasm.

rules: [
{ test: [/\.ts$/], loader: "ts-loader" },
{ test: /\.wasm$/, type: "asset/inline" },
],

The code for the npm package looks like this

import * as tf from '@tensorflow/tfjs';

import opencvWasm from "../resources/custom_opencv.wasm"; // <------ (1)
import opencvWasmSimd from "../resources/custom_opencv-simd.wasm"; // <------ (1)


export class CustomOpenCV {
opencvLoaded = false;
wasm: Wasm | null = null;

init = async (useSimd: boolean) => {
const browserType = getBrowserType();
if (useSimd && browserType !== BrowserType.SAFARI) {
const modSimd = require("../resources/custom_opencv-simd.js"); // <------ (2)
const b = Buffer.from(opencvWasmSimd.split(",")[1], "base64"); // <------ (3)
this.wasm = await modSimd({ wasmBinary: b }); // <------ (4)
} else {
const mod = require("../resources/custom_opencv.js"); // <------ (2')
const b = Buffer.from(opencvWasm.split(",")[1], "base64"); // <------ (3')
this.wasm = await mod({ wasmBinary: b }); // <------ (4')
}
};

predict = async (targetCanvas: HTMLCanvasElement, th1: number, th2: number, apertureSize: number, l2gradient: boolean) => {
<snip>
};
}

(1) imports a wasm file.
(2)(2') imports a js file.
(3)(3') read the wasm data into the buffer.
(4)(4') instatiate the wasm.

That’s all. It’s really easy.

Creating Application using this npm package

If you just want to use it normally, you can import the package and just use new().

import { CustomOpenCV } from "opencv-lib";

const App = () => {
const opencvLib = React.useMemo(() => {
const lib = new CustomOpenCV();
lib.init(false);
<snip>

Repository

The contents described here are stored in the following repository.

Follow the readme and try to run the commands.

The amount of code is small, so you should be able to understand it in about 10 minutes while running it.

Finally

We have introduced how to bundle wasm file.

In tensorflowjs (wasm backend), ffmpeg.wasm, etc., the wasm file is downloaded from CDN. Which method is more preferable is a case-by-case basis, so please use your own judgment.

I am very thirsty!!

--

--