Using js_of_ocaml to write game in 13k for js13k game jam
Published: 2021-01-08 17:16:02

This post is a result of a game I made in OCaml for js13k game jam. I will share some observations I had when creating the game.

But first let me briefly introduce [js13k](https://js13kgames.com/).
js13k is a coding competition for javascript developers. The goal is to create a game in one month that follows the given theme. The challenge is that the whole zip package has to be no more than 13kB in size. Everything that is required to run the game must be included in the package - the game cannot load any resources from the web and cannot access external services.

Since this was my first time using [OCaml](https://ocaml.org/), I wanted to build something simple to make sure I was going to make it and wouldn't go beyond the required size. The theme for this year was "404 not found", so i created a guessing game in which the player needs to reveal a word or a phrase by guessing letters - similar to the game of hangman. This was pretty basic in itself, so I've added animations after each correctly guessed phrase. Each phrase has it's own animation and this turned out to take about 1/4 of the code size.

The submitted game must be implemented in javascript. You can write in any language, as long as it can be transpiled to javascript. I've submitted a pure javascript game in previous years, but this time I wanted to try a language that is compiled and has static typing.
My main issue with javascript is that it's dynamically and weakly typed. This can lead to weird errors that can only be discovered at runtime. It also makes any nontrivial refactoring much harder, because you now need to rerun the application to check if everything is still working. Lastly, static typing has additional advantage - it's a kind of automatic documentation of input and output.
Here's some weird results you can get in javascript:
```javascript
'a' + 1   // 'a1'
'a' - 1   // NaN
'a' + NaN // 'aNaN'
[] == []  // false
[] == ![] // true
```
All of these are either highly surprising or don't make any real sense.

In contrast, OCaml is statically and strongly typed language. This results in constructs like `"a" + 1` simply being a compilation error. It goes even further, because even adding an integer to a float doesn't compile - you need to cast both arguments to the same type. This simplifies development, because it yields no surprising behaviour. Refactoring is also much simpler, because compiler checks the correctness at least partially.

OCaml doesn't compile to javascript out of the box, but there's a library just for that: [js_of_ocaml](https://ocsigen.org/js_of_ocaml/3.7.0/manual/overview).
It plays well with OCaml tooling, you can install it using opam package manager with command
`opam install js_of_ocaml js_of_ocaml-ppx`
`js_of_ocaml` is the base package `js_of_ocaml-ppx` provides syntax extensions for OCaml code. There are a few more packages that provide additional functions.
Once installed, you first compile OCaml code to byte code as you'd normally do, for example with `ocamlc`:
```ocaml
ocamlfind ocamlc -package js_of_ocaml -package js_of_ocaml-ppx -linkpkg -o script.byte script.ml
```
and then you invoke `js_of_ocaml` to generate javascript from OCaml byte code
`js_of_ocaml script.byte`
[dune](https://dune.build/), commonly used build tool for OCaml, also has direct support for compiling OCaml to javascript with `js_of_ocaml`.

Functions provided by `js_of_ocaml` are split to several modules:
`module Js` - javascript bindings
`module Dom_html` - DOM HTML bindings
`module Firebug` - debugging console
and many other

All functions operate on types provided by `js_of_ocaml`, which among other are:
`Js.number`
`Js.date`
`Js.regex`
`Js.js_string`
`Js.opt`
`Js.optdef`

`number`, `date` and `regex` map to the types known from javascript. There's also `js_string`, which is javascript's string type. It provides all methods that javascript string has and therefore is not compatible with OCaml string type. That's why it's often needed to do conversions between the two types.
`opt` and `optdef` are special types for handling null and undefined values: `opt` represents a possibly null value, `optdef` - possibly undefined value. As such, there's no notion of type that can be both null and undefined.

All types in `js_of_ocaml` package are designed in an object oriented way, so you call methods and read/set properties on types. Type signatures of these types are somewhat complex, so to ease that you should use syntax extensions provided by `js_of_ocaml-ppx` package. This gives you syntax that is pretty similar to normal object oriented OCaml code:
```ocaml
element##method_name arg1 arg2 (* method call *)
```
```ocaml
let foo = element##.property (* read value of a property *)
```
```ocaml
element##.property := new_value (* set new value for a property *)
```

For example, here I call `querySelector` to get all elements that have class `big`:
```ocaml
Html.document##querySelector (Js.string ".big")
```

Here's how I can get current color of some DOM element `elem`:
```ocaml
let color = elem##.style##.color
```

And I can change current colour to, e.g. "black":
```ocaml
elem##.style##.color := Js.string "black"
```

I can also assign event handlers:
```ocaml
Html.document##.onkeydown := Html.handler keypressed
```

Here's some less trivial example - a function that adds a CSS class to a given element:
```ocaml
let jstr s = Js.string s
let nullstr () = jstr ""
let addClass (elem: Html.element Js.t) cls =
        let class_str = (jstr "class") in
        let current_cls = (elem##getAttribute class_str |> Js.Opt.get) nullstr in  (* 1 *)
        let new_cls = current_cls##concat_2 (jstr " ") (jstr cls) in  (* 2 *)
        elem##setAttribute class_str new_cls  (* 3 *)
```

At `(* 1 *)` I get current CSS classes assigned to the element. The `getAttribute` method results in a `Js.Opt` type, so it can be null. To get the actual value I pipe it to `Js.Opt.get` function and it returns the stored value if it's there. Otherwise it returns the value returned by `nullstr` function, which is empty string.
At `(* 2 *)` I append the new class to the class list from `(* 1 *)`.
At `(* 3 *)` I finally set the newly constructed string as current for the element.

One thing to keep an eye on is the resulting package size. When all you have is 13kB, even bytes can count. Unfortunately, `js_of_ocaml` adds about ~6kB of overhead upfront. That's quite a lot, especially since it doesn't provide any extra features to create a web based game. And that can grow even more if using some additional OCaml functions. When I tried to use `Printf.printf` function, resulting package size increased by ~15kB. I had to fall back to simple string concatenation.
One more case when I had an issue with code size was when I had to compare OCaml string with `Js.js_string`. To do that I needed to convert one of them so that both have the same type. When I converted OCaml string to `Js.js_string` package size was bigger than when I converted `Js.js_string` to Ocaml string. The difference was ~4kB, so was definitely noticeable.

To sum it up, it was quite fun to try to make a javascript game in OCaml. Having a strict type checker definitely helped. I had only one runtime error overall - and only because I did some silly typo on CSS class name. This wouldn't be possible in pure javascript. Iteration cycle was pretty quick, but obviously slower than in javascript, because there's no compilation step. Compilation time was quite quick, but noticeable.
The main issue is generated javascript code size, which can rise quite quickly in some situations if you're not careful. But if javascript code size doesn't matter - and it usually doesn't - `js_of_ocaml` is viable way of writing javascript code.