zemscripten updated to zig master
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-07-03 16:07:25 -06:00
.github/workflows upgrade setup-zig 2025-07-20 22:22:30 +01:00
src refAllDecls 2026-07-03 16:07:25 -06:00
.gitignore Update .gitignore 2024-11-03 21:35:32 +00:00
build.zig Update build script for zig 0.17 compatibility 2026-06-27 14:49:21 +02:00
build.zig.zon upgrade setup-zig 2025-07-20 22:22:30 +01:00
LICENSE files from monorepo @ 613d847104a072fc1d4442f58d5f50b3f9c3f27c 2024-11-03 20:41:00 +00:00
README.md Update README 2025-12-10 00:13:47 +00:00

zemscripten

Zig build package and shims for Emscripten emsdk

How to use it

Add zemscripten and (optionally) emsdk to your build.zig.zon dependencies

    zig fetch --save=emsdk https://github.com/emscripten-core/emsdk/archive/refs/tags/4.0.3.tar.gz

Emsdk must be activated before it can be used. You can use activateEmsdkStep to create a build step for that:

    const activate_emsdk_step = @import("zemscripten").activateEmsdkStep(b);

Add zemscripten's "root" module to your wasm compile target., then create an emcc build step. We use zemscripten's default flags and settings which can be overridden for your project specific requirements. Refer to the emcc documentation. Example build.zig code:

    const wasm = b.addStaticLibrary(.{
        .name = "MyGame",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    const zemscripten = b.dependency("zemscripten", .{});
    wasm.root_module.addImport("zemscripten", zemscripten.module("root"));

    const emcc_flags = @import("zemscripten").emccDefaultFlags(b.allocator, .{
        .optimize = optimize,
        .fsanitize = false,
    });

    var emcc_settings = @import("zemscripten").emccDefaultSettings(b.allocator, .{
        .optimize = optimize,
        .emsdk_allocator = .emmalloc,
    });
    try emcc_settings.put("ALLOW_MEMORY_GROWTH", "1");

    const emcc_step = @import("zemscripten").emccStep(
        b,
        &.{ }, // src file paths 
        &.{ wasm }, // src compile steps
        .{
            .optimize = optimize,
            .flags = emcc_flags,
            .settings = emcc_settings,
            .use_preload_plugins = true,
            .embed_paths = &.{},
            .preload_paths = &.{},
            .shell_file_path = null, // set this to override the default html shell
            .js_library_path = null,
            .out_file_name = "MyGame.html", // or "MyGame.js"
            .install_dir = .{ .custom = "web" },
        },
    );
    emcc_step.dependOn(activate_emsdk_step);

    b.getInstallStep().dependOn(emcc_step);

Now you can use the provided Zig panic and log overrides in your wasm's root module and define the entry point that invoked by the js output of emcc (by default it looks for a symbol named main). For example:

const std = @import("std");

const zemscripten = @import("zemscripten");
pub const panic = zemscripten.panic;

pub const std_options = std.Options{
    .logFn = zemscripten.log,
};

export fn main() c_int {
    std.log.info("hello, world.", .{});
    return 0;
}

You can also define a run step that invokes emrun. This will serve the html locally over HTTP and try to open it using your default browser. Example build.zig code:

    const html_filename = try std.fmt.allocPrint(b.allocator, "{s}.html", .{wasm.name});

    const emrun_args = .{};
    const emrun_step = @import("zemscripten").emrunStep(
        b,
        b.getInstallPath(.{ .custom = "web" }, html_filename),
        &emrun_args,
    );

    emrun_step.dependOn(emcc_step);

    b.step("emrun", "Build and open the web app locally using emrun").dependOn(emrun_step);

See the emrun documentation for the difference args that can be used.