Wasm_of_ocaml

Overview

Wasm_of_ocaml is a compiler from OCaml bytecode programs to WebAssembly. It provides an alternative way to run pure OCaml programs in JavaScript environments like browsers and Node.js.

The compiler is provided by the wasm_of_ocaml-compiler package. The Js_of_ocaml libraries are compatible with this compiler.

Installation

The easiest way to install wasm_of_ocaml is to use opam:

opam install wasm_of_ocaml-compiler js_of_ocaml js_of_ocaml-ppx js_of_ocaml-lwt

Binaryen version 119 or later is required. The opam command above will install it automatically if needed.

Usage

Your program must first be compiled using the OCaml bytecode compiler ocamlc. JavaScript bindings are provided by the js_of_ocaml package and the syntax extension by the js_of_ocaml-ppx package:

ocamlfind ocamlc -package js_of_ocaml -package js_of_ocaml-ppx \
    -linkpkg -o cubes.byte cubes.ml

Then, run the wasm_of_ocaml compiler to produce Wasm code:

wasm_of_ocaml cubes.byte

This produces a JavaScript loading script cubes.js and a directory cubes.assets containing the Wasm code.

With dune

Dune has native support for wasm_of_ocaml (starting with dune 3.17.0). It supports both standard and separate compilation. See the dune documentation.

Supported features

Most of the OCaml standard library is supported. However:

Extra libraries distributed with OCaml (such as Thread) are not supported in general. However:

Differences from js_of_ocaml

Compared to js_of_ocaml:

Effect handlers

OCaml 5.x code using effect handlers can be compiled in three ways:

The CPS transformation is not the default since the generated code is slower, larger, and less readable. However, the JSPI extension is currently only available in Chrome 137 and Node.js 25 (or higher), and performing effects is slower when using it. Use --effects=cps for other browsers.

The native implementation is based on the WebAssembly typed continuations proposal (stack switching). It provides the best performance but requires a runtime with support for the WasmFX extension (currently available, behind the --experimental-wasm-wasmfx flag, in Chrome 148 or higher, or in a recent Node.js canary release (V8 version 14.7.100 or higher)).

WASI support

You can produce a WASI binary by running wasm_of_ocaml with the --enable wasi flag. At the moment, wasm_of_ocaml supports WASI 0.1. Features from the Sys and Unix modules are available whenever they're supported by the WASI API.

The binaries produced by wasm_of_ocaml require the GC and exception-handling proposals, which are supported by Node.js, Wasmtime (with the -W=all-proposals=y flag), and the Wizard engine. Wasmtime does not support the legacy Wasm exception-handling instructions, so when --enable wasi is used the compiler emits the new exnref-based instructions instead.

When native effect handlers are used (--effects native), the binaries additionally require the stack-switching proposal. It is supported by Node.js and by the Wizard engine (with the --ext:stack-switching flag), but not by Wasmtime.

For now, the output remains the same as without the --enable wasi flag: a JavaScript file foo.js and a directory foo.assets containing the Wasm code code.wasm. The JavaScript file can be used to run the WASI binary with node, while the Wasm code can be run directly by other Wasm engines.

Limitations

The WASI target currently has the following limitations:

Binding with JavaScript libraries

Js_of_ocaml lets you bind code with JavaScript libraries by linking .js files. Similarly, wasm_of_ocaml allows linking Wasm modules (.wasm or .wat files). See Writing Wasm primitives.

If a js_of_ocaml project uses external primitives defined in companion .js files, you need to implement the same primitives in Wasm modules to build with wasm_of_ocaml.

Toplevel

Wasm_of_ocaml can compile an OCaml toplevel (REPL) to WebAssembly. The toplevel dynamically compiles OCaml bytecode to Wasm and instantiates it in the browser.

See Toplevel and Dynlink for build instructions.

OCaml's Dynlink module is supported. Plugin .cmo files are compiled on the fly to WebAssembly and instantiated at runtime.

See Toplevel and Dynlink for build instructions.

Pseudo filesystem

The pseudo filesystem is supported. Use the --file flag to embed files, the same way as with js_of_ocaml:

wasm_of_ocaml --file data.txt cubes.byte

See also