This post is introducing yet another way to run OCaml codes in the browser. It’s about compiling OCaml codes into bytecode and run it with the virtual machine in WASM.
Currently, js_of_ocaml is the most popular method to run OCaml in the browser. Js_of_caml have a great ecosystem. But it has its limitations.
Another disadvantage of js_of_ocaml is that it’s hard for it to interface with C codes. A lot of OCaml modules depend on C codes. Some of them provide JS stubs, some do not, but all provide C stubs. One week ago, I wrote a library in OCaml with some C libraries, and I want to adapt it to the web. I found it’s hard for me to implement. I compiled my code with Emscripten and invoke it from js stubs. It’s hacky. So I imagined running OCaml directly on WASM.
And I found this repo. It’s a great idea. It’s OK for this repo to run a Hello World demo or to run pure OCaml code. But it’s not good enough if you want to link C libraries. You can’t even use popular libraries such as Core_kernel. Also, I found this repo is inactive. So I decided to reinvent the vehicle.
I forked the official OCaml and hacked its runtime. I was adding support of libraries such as Unix library, Ctypes, Base, Core_kernel. All of them are embedded in the binary because it’s not easy for Emscripten to build dynamic modules.
Why not drop your OCaml bytecode to try?
I draw a picture to demonstrate the differences between js_of_ocaml vs WASM runtime. It’s easy to know the relations between OCaml codes and stubs.
At last, we can get a single WASM file(about 2Mb) and a js glue file(about 200kb). They include a complete OCaml runtime, and they can run a lot of popular OCaml programs. And I can link my C libraries through Ctypes, which are very extensible.
After I get the WASM file, I wonder about its performance. It’s OCaml running in a virtual machine(OCaml runtime) running in another virtual machine(WebAssembly). Will it be slow? How it performs compared to js_of_caml.
So I use both of them to run the ocamlc(the compiler of OCaml) to compute the dependencies of the standard library.
node ../boot/ocamlc.js -depend *.ml *.mli 1.80s user 0.10s system 145% cpu 1.296 total
node ../runtime/ocamlrun.js ../boot/ocamlc -depend *.ml *mli 1.90s user 0.03s system 112% cpu 1.720 total
Js_of_caml would be a little faster than my WASM version. Well, it would be a little frustrating. I would say js_of_ocaml is still an excellent solution to run OCaml in the browser. Since the gap is not very big, the WASM way is still worth a try. This method can be used on mobile platform such ad iOS and other strict platforms. It’s portable.
I am looking forward to the “native” WASM version of OCaml. And I don’t know if the OCaml team is planning it. Would you please tell me?
But there are also some troubles. Using this method, you have to customize a runtime for your application. But it’s not a happy job. It’s complex and annoying. Once you add a library with C stubs, you have to rebuild the runtime again. So I imagine a tool can automatically extract the C stubs from OCaml libraries and embedded them in the runtime. It’s fancy, but I don’t know if it would be too hard to implement. But currently, I can enjoy the runtime to run my application. If you have any ideas, please tell me.