Zig WebAssembly

I’m trying to create a basic math library in Webassembly using Zig, and use it in Node.js, following this tutorial:

I created this math.zig file:

export fn add(n1: i32, n2: i32) i32 {
    return n1 + n2;
}

and created the wasm library with:
zig build-lib math.zig -target wasm32-freestanding -dynamic

I then created this node.js file (index.js):

const fs = require("fs");

const wasmBuffer = fs.readFileSync("./math.wasm");

WebAssembly.instantiate(wasmBuffer).then((wasmModule) => {
    console.log(wasmModule.instance.exports.add(1, 2));
});

and ran it with “node index.js”, but always get the error:
TypeError: wasmModule.instance.exports.add is not a function

I’m pretty sure I’m king some mistake generating the WASM library from Zig. It seems the add function doesn’t get exported.

1 Like

I believe that there was a recent change to now use build-exe with -fno-entry for wasm targets, not build-lib with -dynamic.

2 Likes

In addition to this, you need to explicitly state which exported functions/variables you want to include in the Wasm output, either by passing -rdynamic to include everything, or by repeatedly passing the --entry=<symbol> for each symbol you want to include.

So the complete invocation is

zig build-exe ./math.zig -target wasm32-freestanding -fno-entry -rdynamic
2 Likes

with Zig 0.11.0 I get “error: unrecognized parameter: ‘-fno-entry’”

Sorry, -fno-entry was only recently added to the 0.12.0-dev master branch of Zig. I would personally recommend you to download and use the master version of Zig since Zig is a fast-evolving language and ecosystem and information gets outdated very quickly, but if you want to stick with 0.11.0 I believe

zig build-lib ./math-zig target wasm32-freestanding -dynamic -rdynamic

should work.

1 Like

It worked, thanks. So I’d better study Zig with master branch instead 0f 0.11.0?

The official “getting started” docs encourages using the master branch and beginner resources like ziglearn.org or Ziglings also seem to agree. When asking for help and advice people will generally assume you’re on the latest master unless you explicitly state a different version.

3 Likes

On a project I did a while back using 0.11, I had this in my build.zig and just tested it and produces a wasm module in zig-out/lib by running zig build:

    const lib = b.addSharedLibrary(.{
        .name = "mywasm",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = .{
            .cpu_arch = .wasm32,
            .os_tag = .freestanding,
        },
        .optimize = .ReleaseSmall,
    });
    lib.export_symbol_names = &.{ "alloc", "free", "add", "sub", "zlog" };
    b.installArtifact(lib);

I think the lib.export_symbol_names is the key here.

2 Likes

Pardon the self-advertisement, but check out my project if you want to use Zig in JavaScript projects: Zigar.

It’s designed to make the process as seamless as possible. Just set up the rollup plugin and it’ll generate the WASM file for you. For Node you also have to option to run Zig code as native code (doen’t work in Node 20+ currently).

At the Pb2Zig project page you’ll find some nifty demos showing what can be done using Zig + WebAssembly.

3 Likes