# WebAssembly

### WebAssembly

WebAssembly, often shortened to Wasm, is a portable binary instruction format. It lets you compile code once and run it inside different hosts, such as web browsers, servers, embedded runtimes, and command-line Wasm engines.

Zig can compile to WebAssembly. This makes Zig useful for small browser modules, plugin systems, sandboxed code, portable command-line logic, games, interpreters, and compute-heavy functions that need predictable low-level control.

A normal Zig program has a `main` function:

```zig
const std = @import("std");

pub fn main() void {
    std.debug.print("Hello\n", .{});
}
```

A WebAssembly module often exports functions instead:

```zig
export fn add(a: i32, b: i32) i32 {
    return a + b;
}
```

Build it:

```bash
zig build-lib add.zig -target wasm32-freestanding -dynamic -rdynamic
```

This produces a `.wasm` file.

#### What WebAssembly Is

WebAssembly is not JavaScript. It is also not a full operating system. It is a compact binary format for code.

A Wasm module usually contains:

```text
compiled functions
linear memory
imports
exports
tables
globals
```

The host loads the module, provides any imports it needs, and calls its exported functions.

For example, your Zig code may export this:

```zig
export fn square(x: i32) i32 {
    return x * x;
}
```

Then JavaScript, a server runtime, or another host can call `square`.

#### WebAssembly Has a Host

A normal native program runs directly on an operating system.

A WebAssembly module runs inside a host.

Examples of hosts include:

```text
web browser
Wasmtime
Wasmer
WasmEdge
Node.js
Deno
custom application runtime
```

This matters because Wasm does not automatically have access to files, sockets, clocks, environment variables, or the terminal. The host decides what the module can do.

This is one reason WebAssembly is useful for sandboxing. A module can be given only the capabilities the host wants to provide.

#### `wasm32-freestanding`

A common Zig target for simple Wasm modules is:

```text
wasm32-freestanding
```

The `wasm32` part means 32-bit WebAssembly.

The `freestanding` part means there is no normal operating system interface.

This target is good for small exported functions:

```zig
export fn multiply(a: i32, b: i32) i32 {
    return a * b;
}
```

Build:

```bash
zig build-lib math.zig -target wasm32-freestanding -dynamic -rdynamic
```

The result is a Wasm module that exports functions.

#### Exported Functions

A function must be exported if the host needs to call it.

```zig
export fn add(a: i32, b: i32) i32 {
    return a + b;
}
```

The `export` keyword makes the function visible outside the module.

Keep exported function signatures simple at first. Use integers and floats before trying strings, slices, structs, or pointers.

Good beginner exports:

```zig
export fn add(a: i32, b: i32) i32 {
    return a + b;
}

export fn is_even(x: i32) bool {
    return x % 2 == 0;
}
```

More advanced exports need memory coordination between Zig and the host.

#### WebAssembly Memory

WebAssembly uses linear memory. You can think of it as one large byte array.

A pointer in Wasm is usually an offset into this memory.

For example, a Zig slice:

```zig
[]const u8
```

cannot be passed directly to JavaScript as a normal JavaScript string. The host needs to know where the bytes are and how many bytes to read.

That usually means passing two values:

```text
pointer
length
```

A common pattern is:

```zig
export fn get_message_ptr() [*]const u8 {
    return "hello".ptr;
}

export fn get_message_len() usize {
    return "hello".len;
}
```

The host calls both functions, then reads bytes from Wasm memory.

This is more manual than calling a normal native function, but it is also explicit.

#### Strings Are Bytes

In Zig, string literals are UTF-8 bytes.

```zig
const msg = "hello";
```

In WebAssembly, the host must read those bytes from module memory and decode them if it wants a host-language string.

That means string exchange usually needs three pieces:

```text
memory export
pointer
length
```

For beginners, avoid passing strings across the Wasm boundary until you understand memory exports.

Start with numbers. Then move to buffers.

#### Allocating Memory for the Host

Sometimes the host needs to pass data into the Zig module. For that, the module can export allocation functions.

Example:

```zig
const std = @import("std");

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();

export fn alloc(len: usize) ?[*]u8 {
    const buf = allocator.alloc(u8, len) catch return null;
    return buf.ptr;
}

export fn free(ptr: [*]u8, len: usize) void {
    allocator.free(ptr[0..len]);
}
```

The host can call `alloc`, write bytes into Wasm memory, call another exported function, then call `free`.

This is a common pattern, but it requires discipline. The host and module must agree on ownership.

The rule is simple: whoever allocates should usually provide the matching free function.

#### No Normal Files by Default

On native Linux, macOS, or Windows, this kind of code can open a file:

```zig
const file = try std.fs.cwd().openFile("data.txt", .{});
```

On `wasm32-freestanding`, there is no normal filesystem.

That does not mean Wasm can never use files. It means the host must provide file access through imports or through a system interface such as WASI.

#### WASI

WASI stands for WebAssembly System Interface.

It defines a standard way for WebAssembly modules to access system-like features, such as files, clocks, random data, and environment variables, when the host allows them.

A target may look like:

```text
wasm32-wasi
```

Use WASI when you want a WebAssembly module that behaves more like a small command-line program.

Use freestanding Wasm when you want a minimal module with explicit imports and exports.

A useful mental split:

```text
wasm32-freestanding: small module, no OS assumptions
wasm32-wasi: portable system interface, more like a CLI program
```

#### Calling Wasm from JavaScript

Suppose you have this Zig file:

```zig
export fn add(a: i32, b: i32) i32 {
    return a + b;
}
```

Build:

```bash
zig build-lib add.zig -target wasm32-freestanding -dynamic -rdynamic
```

A JavaScript host can load the module and call it:

```js
const bytes = await fetch("add.wasm").then((res) => res.arrayBuffer());
const module = await WebAssembly.instantiate(bytes);

console.log(module.instance.exports.add(20, 22));
```

Output:

```text
42
```

This is the simplest kind of Zig-Wasm boundary: numbers in, number out.

#### Calling Wasm from a Runtime

Browsers are not the only Wasm hosts.

With a runtime such as Wasmtime, you can run Wasm from the command line or embed it in a server application.

The same core idea applies:

```text
load module
provide imports
call exports
read memory if needed
```

Wasm is a portable execution format. The details depend on the host.

#### Limitations

WebAssembly is powerful, but it has limits.

You do not automatically get threads, sockets, files, or operating system APIs.

You do not pass complex Zig data structures across the boundary directly.

You must design the interface between the host and the module.

Debugging can be different from native debugging.

Performance is good for many compute-heavy tasks, but crossing the host-module boundary too often can be expensive.

Because of this, Wasm works best when the interface is small and clear.

Good use:

```text
parse this buffer
compress this data
run this algorithm
validate this input
simulate this step
render this frame
```

Poor use:

```text
call into Wasm for every tiny object property
move many small strings back and forth
depend on hidden host behavior
```

#### Designing a Good Wasm API

A good Wasm API is usually coarse-grained.

Instead of this:

```text
call add_byte one million times
```

Prefer this:

```text
pass one buffer
process the whole buffer
return one result
```

That reduces boundary overhead.

Use simple exported functions:

```zig
export fn process(ptr: [*]u8, len: usize) usize {
    const data = ptr[0..len];

    var count: usize = 0;
    for (data) |byte| {
        if (byte == '\n') count += 1;
    }

    return count;
}
```

This function receives a pointer and length, views them as a slice, counts newline bytes, and returns the count.

The host is responsible for putting the data into Wasm memory before calling `process`.

#### Complete Example

Here is a small Zig Wasm module:

```zig
export fn add(a: i32, b: i32) i32 {
    return a + b;
}

export fn square(x: i32) i32 {
    return x * x;
}

export fn max(a: i32, b: i32) i32 {
    if (a > b) return a;
    return b;
}
```

Save it as `math.zig`.

Build it:

```bash
zig build-lib math.zig -target wasm32-freestanding -dynamic -rdynamic
```

This creates a Wasm module.

A JavaScript host can call it:

```js
const bytes = await fetch("math.wasm").then((res) => res.arrayBuffer());
const wasm = await WebAssembly.instantiate(bytes);

const api = wasm.instance.exports;

console.log(api.add(2, 3));
console.log(api.square(9));
console.log(api.max(10, 4));
```

Possible output:

```text
5
81
10
```

This example avoids strings, files, allocators, and complex memory rules. That is the right starting point.

#### The Practical View

WebAssembly is best understood as a small, portable execution target. Zig compiles well to it because Zig already makes memory, types, exports, and platform boundaries explicit.

Start with pure functions that take numbers and return numbers. Then learn linear memory. Then learn pointer-plus-length buffer passing. After that, learn allocation, WASI, host imports, and browser or runtime integration.

For Zig beginners, WebAssembly teaches an important lesson: when there is no normal operating system underneath your program, every dependency must be made explicit.

