Skip to content

Instantly share code, notes, and snippets.

@surma
Last active March 8, 2024 12:06
Show Gist options
  • Save surma/b2705b6cca29357ebea1c9e6e15684cc to your computer and use it in GitHub Desktop.
Save surma/b2705b6cca29357ebea1c9e6e15684cc to your computer and use it in GitHub Desktop.
webpack-emscripten-wasm

Minimal example making webpack and wasm/Emscripten work together.

Build instructions:

  • Clone this gist
  • npm install
  • npm start
  • Open http://localhost:8080
  • Look at console

Note: Docker is required to build this project.

I filed a bug with webpack to make this integration easier.

More questions? Hit me up on Twitter.

License Apache-2.0

<3 Surma

/**
* Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <emscripten.h>
EMSCRIPTEN_KEEPALIVE
int fib(int n) {
int i, t, a = 0, b = 1;
for (i = 0; i < n; i++) {
t = a + b;
a = b;
b = t;
}
return b;
}
<!doctype html>
<script src="./dist/bundle.js"></script>
/**
* Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fibonacci from './fibonacci.js';
import fibonacciModule from './fibonacci.wasm';
// Since webpack will change the name and potentially the path of the
// `.wasm` file, we have to provide a `locateFile()` hook to redirect
// to the appropriate URL.
// More details: https://kripken.github.io/emscripten-site/docs/api_reference/module.html
const module = fibonacci({
locateFile(path) {
if(path.endsWith('.wasm')) {
return fibonacciModule;
}
return path;
}
});
module.onRuntimeInitialized = () => {
console.log(module._fib(12));
};
{
"name": "lol",
"scripts": {
"build:codec": "docker run --rm -v $(pwd):/src trzeci/emscripten emcc -O3 -s WASM=1 -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"cwrap\"]' -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s 'EXPORT_NAME=\"fibonacci\"' -o ./fibonacci.js fibonacci.c",
"build:bundle": "webpack",
"build": "npm run build:codec && npm run build:bundle",
"serve": "http-server",
"start": "npm run build && npm run serve"
},
"devDependencies": {
"exports-loader": "^0.7.0",
"file-loader": "^1.1.11",
"http-server": "^0.11.1",
"webpack": "^4.8.3",
"webpack-cli": "^2.1.3"
}
}
/**
* Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const webpack = require("webpack");
const path = require("path");
module.exports = {
mode: "development",
context: path.resolve(__dirname, "."),
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js"
},
// This is necessary due to the fact that emscripten puts both Node and web
// code into one file. The node part uses Node’s `fs` module to load the wasm
// file.
// Issue: https://github.com/kripken/emscripten/issues/6542.
browser: {
"fs": false
},
module: {
rules: [
// Emscripten JS files define a global. With `exports-loader` we can
// load these files correctly (provided the global’s name is the same
// as the file name).
{
test: /fibonacci\.js$/,
loader: "exports-loader"
},
// wasm files should not be processed but just be emitted and we want
// to have their public URL.
{
test: /fibonacci\.wasm$/,
type: "javascript/auto",
loader: "file-loader",
options: {
publicPath: "dist/"
}
}
]
},
};
@dhdaines
Copy link

dhdaines commented May 6, 2022

No longer works with Webpack 5 - instead of the node configuration, you need something like:

module.exports = {
    resolve: {
        fallback: {
            crypto: false,
            fs: false,
            path: false
        }
    }
};

For the wasm files, one solution is just to use copy-webpack-plugin, which is annoying since it makes it non-transparent for people to use your module, but then again, they're already adding the magic resolve incantation...

const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
    plugins: [
        new CopyPlugin({
          patterns: [
              { from: "node_modules/MYPACKAGE/MYPACKAGE.wasm*",
                to: "[name][ext]" },
          ],
      }),
    ],
};

@9oelM
Copy link

9oelM commented May 10, 2022

Hi guys, I felt extremely lost while I was digging into this issue, so after I got everything working, I made a example repo containing detailed walkthroughs:

Hope it helps someone.

@acransac
Copy link

With Webpack 5, I think you can remove the locateFile hook, the file-loader dependency and simplify

    import fibonacciModule from './fibonacci.wasm';

to

    import './fibonacci.wasm';

provided that you change the .wasm rule in the Webpack config to:

    {
        test: /fibonacci\.wasm$/,
        type: "asset/resource",
        generator: {
            filename: "[name].wasm"
        }
    }

It might help with issues locating the wasm asset when loading the page.

@spencer-sherk-ai
Copy link

Hi guys, I felt extremely lost while I was digging into this issue, so after I got everything working, I made a example repo containing detailed walkthroughs:

Hope it helps someone.

Thanks! You just saved me a ton of time with this. Confirmed still working as of 2/2/2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment