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.
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.
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.mlThen, 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.
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.
Most of the OCaml standard library is supported. However:
Sys module is not supportedExtra libraries distributed with OCaml (such as Thread) are not supported in general. However:
Bigarray - supported using Typed ArraysStr - supportedGraphics - partially supported using canvas (see also js_of_ocaml-lwt.graphics)Unix - time-related functions are supportedCompared to js_of_ocaml:
jspi (instead of disabled)--effects flag accepts jspi instead of double-translationbuild-fs subcommand; use the --file flag insteadOCaml 5.x code using effect handlers can be compiled in three ways:
--effects=cps - Uses the CPS transformation from js_of_ocaml--effects=jspi (default) - Uses the JavaScript-Promise Integration extension--effects=native - Uses the WebAssembly Stack Switching proposalThe 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)).
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.
The WASI target currently has the following limitations:
js_of_ocaml library (Js, Js.Unsafe, the DOM bindings, XMLHttpRequest, ...) cannot be used.Weak array or an Ephemeron are kept alive and never reclaimed by the garbage collector.Marshal and output_value run in time quadratic in the number of heap blocks serialised.Dynlink library is not supported.fork, exec, ...) and no signals; Sys.command and Unix.rewinddir are not implemented. File system access is restricted to the directories preopened by the host (the node launcher preopens . and /tmp).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.
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.
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