# WebAssembly with Elide

> Run WASI command modules and load WASM libraries from JavaScript and TypeScript

WebAssembly support is experimental in 1.6.0.

| You have | Use | Result |
| --- | --- | --- |
| A WASI command | `elide run app.wasm` | Runs the module with WASI Preview 1 imports. |
| A WASM library | The `WebAssembly` API or an ES module import from JS/TS | Exposes the module's exports. |

<img src="https://elide.help/assets/demos/wasm.gif" alt="elide app.wasm — WASI commands and WASM libraries from JavaScript" width="1591" height="546" loading="lazy" decoding="async" class="rounded-lg border border-border my-6 w-full" />

To compile Java or Kotlin to WebAssembly, see [WebImage](https://elide.help/docs/toolchain/jvm/webimage); its output needs its JavaScript
loader and does not run with `elide run`.

## Run a WASM file

```bash
elide run app.wasm
elide app.wasm
elide run app.wasm -- first second   # program arguments
elide run --timeout=5s app.wasm      # exit 124 after 5 seconds
```

Elide calls the module's exported `_start`, or `_main` if `_start` is absent; a module with neither fails, so load it
from JavaScript instead.

| Behavior | Value |
| --- | --- |
| Argument zero | The module's absolute path. |
| Exit status | Set by WASI `proc_exit`, not by a function's return value. |
| Traps, malformed binaries | Nonzero exit. |
| `--timeout` | Covers instantiation and execution; exits `124`. |

### Project entrypoints

Name a module as the project entrypoint in `elide.pkl`, then run `elide run` in the project:

```pkl
amends "elide:project.pkl"
name = "wasm-app"
entrypoint { "app.wasm" }
```

See [Elide Projects](https://elide.help/docs/projects).

## WASI

Commands receive **WASI Snapshot Preview 1** imports under `wasi_snapshot_preview1`: standard streams,
arguments, exit status, and filesystem operations. Libraries loaded from JavaScript do not get WASI.

### Directory access

WASI commands cannot access host directories until you map one:

```bash
elide -Xpolyglot.wasm.WasiMapDirs=work::./data run app.wasm
```

This exposes `./data` as the guest directory `work`. Separate mappings with commas:
`work::./data,output::./results`.

The sandbox still applies, and WASI file opens need both read and write permission, even for reads:

```bash
elide -Xpolyglot.wasm.WasiMapDirs=work::./data run \
  --sandbox --allow-read=./data --allow-write=./data app.wasm
```

See [Sandbox and Limits](https://elide.help/docs/sandbox).

## JavaScript and TypeScript interop

The global `WebAssembly` API needs no flag:

```javascript
import { readFile } from "node:fs/promises";

const bytes = await readFile(new URL("./math.wasm", import.meta.url));
const { instance } = await WebAssembly.instantiate(bytes, {});
console.log(instance.exports.add(20, 22));
```

Elide does not call `_start` or `_main` on modules loaded from JavaScript. The second argument supplies the
module's imports:

```javascript
const imports = { env: { log: (value) => console.log(value) } };
const { instance } = await WebAssembly.instantiate(bytes, imports);
```

The API includes `validate`, `compile`, `Module`, `Instance`, `Memory`, `Table`, `Global`, and import and export
inspection. A compiled `Module` can be instantiated more than once. File reads follow the same permissions as other
JS code.

### ES module imports

```javascript
import { add } from "./math.wasm";
console.log(add(20, 22));
```

```javascript
const math = await import("./math.wasm");
console.log(math.add(20, 22));
```

Specifiers can be relative or absolute paths, `file:` URLs, or package exports that resolve to `.wasm`. A
source-phase import gives you the compiled module to instantiate yourself:

```javascript
import source mathModule from "./math.wasm";
const math = await WebAssembly.instantiate(mathModule, {});
console.log(math.exports.add(20, 22));
```

## Supported WebAssembly proposals

Defaults for both commands and JS/TS interop:

| Feature / proposal | Standard distribution | With Python installed |
| --- | --- | --- |
| Mutable globals, sign-extension operators | Enabled | Enabled |
| Non-trapping float-to-integer conversions | Enabled | Enabled |
| Multi-value function and block results | Enabled | Enabled |
| Bulk memory operations and reference types | Enabled | Enabled |
| Multiple memories | Enabled | Enabled |
| Extended constant expressions | Enabled | Enabled |
| Exception handling | Enabled | Enabled |
| Typed function references | Enabled | Enabled |
| Garbage-collected types (WasmGC) | Enabled | Enabled |
| Tail calls | Enabled | Enabled |
| Fixed-width SIMD (`v128`) | Enabled | Disabled |
| Relaxed SIMD | Enabled | Disabled |

Off by default:

| Proposal | Option | Notes |
| --- | --- | --- |
| Memory64 | `wasm.Memory64` | Also requires `wasm.UseUnsafeMemory`. |
| Threads and atomics | `wasm.Threads` | No WASI thread-spawning host API is supplied. |
| Wide integer arithmetic | `wasm.WideArithmetic` | Separate from ordinary `i64` arithmetic. |
| Legacy exception handling | `wasm.LegacyExceptions` | The newer exception-handling proposal is on by default. |

Enable one with `-Xpolyglot.<option>=true`, for example `-Xpolyglot.wasm.WideArithmetic=true`. Enabling a proposal
does not supply the host imports a module needs.

## Limits

- **Binary modules only.** No `.wat`, no compiling C or Rust, no WASM REPL.
- **WASI Preview 1 only.** No WASI Preview 2, component model, or `node:wasi`; no general network access.
- **Host imports.** `elide run` supplies only WASI; modules that expect browser APIs or Emscripten glue need a host
  that supplies them.
- **SIMD with Python.** With Python installed, SIMD modules are rejected. Use an install without Python, or a
  scalar build of the module.
