Hello Here is the latest OCaml Weekly News, for the week of September 22 to 29, 2026. Table of Contents ───────────────── 2nd release elm_playground (a game engine for beginners) moonpool 0.12 tw 1.1.0, Tailwind CSS in OCaml Cascade: A Typed CSS Toolkit in OCaml ppx_deriving_{yaml,ezjsonm,yamlx} 0.5.0 kqueue-ml 0.5.0 Windtrap: one library for all your OCaml tests Ocsigen Server 8.0.0 Old CWN 2nd release elm_playground (a game engine for beginners) ════════════════════════════════════════════════════════ Archive: Yoann Padioleau announced ───────────────────────── It is my pleasure, again, to announce the second release of `elm_playground', an OCaml package that allows you to easily create /pictures/, /animations/, and even /video games/ in a portable way using an API that really simplifies how to view the computer and its devices (the screen, keyboard, and mouse). The library offers a native backend to run the games from a terminal and a web backend to run the games in your browser. This is a port of the excellent Elm playground package to OCaml. You can install it via OPAM via `opam install elm_playground; opam install elm_playground_native'. The main changes are most of the bugs in the first release have been fixed (mostly by Claude Code), and that now the native and web backend are fully working on all the examples and games. Here is for instance how to play Tetris compiled by jsoo: Here are a few examples of code using the library. First a “picture” app: ┌──── │ (* from https://elm-lang.org/examples/picture *) │ open Playground │ │ let app = │ picture [ │ rectangle brown 40. 200. │ |> move_down 80.; │ circle green 100. │ |> move_up 100.; │ ] │ │ let main = Playground_platform.run_app app └──── Then an “animation” app: ┌──── │ (* from https://elm-lang.org/examples/animation *) │ open Playground │ │ let view time = [ │ octagon darkGray 36. │ |> move_left 100. │ |> rotate (spin 3. time); │ octagon darkGray 36. │ |> move_right 100. │ |> rotate (spin 3. time); │ rectangle red 300. 80. │ |> move_up (wave 50. 54. 2. time) │ |> rotate (zigzag (-. 2.) 2. 8. time); │ ] │ │ let app = │ animation view │ │ let main = Playground_platform.run_app app └──── And finally a “game” app: ┌──── │ (* from https://elm-lang.org/examples/mouse *) │ open Playground │ │ let view _computer (x, y) = [ │ square blue 40. │ |> move x y │ ] │ │ let update computer (x, y) = │ (x +. to_x computer.keyboard, y +. to_y computer.keyboard) │ │ let app = │ game view update (0., 0.) │ │ let main = Playground_platform.run_app app └──── Note that you can write more complex games. For example here is a screenshot of a toy tetris app: For more information, follow the README at See also the code of some classic examples and try them online thx to jsoo: moonpool 0.12 ═════════════ Archive: Simon Cruanes announced ─────────────────────── Howdy, I just released moonpool 0.12, now coming to an opam repository near you. Moonpool (first announced [here]) is a threadpool library for OCaml 5, leveraging domains to actually go brrrrrrr on multiple cores. The (still unique‽) design of moonpool allows the user to create multiple pools, whose cumulative size exceeds `Domain.recommended_domain_count()' without slowdown. It does that by using… threads… instead of domains. Yep. Anyway there's a whole bunch of bugfixes (including one to correct pathological memory usage in #45, big up @gasche as usual :heart_hands:), support for `ambient-context', some cleanup, dscheck for tests, etc. Cheerios on you and your cereal bowls [here] tw 1.1.0, Tailwind CSS in OCaml ═══════════════════════════════ Archive: Thomas Gazagnaire announced ─────────────────────────── I am happy to announce the release of [tw] 1.1.0 (available on brew already and soon in [opam]). ┌──── │ $ brew install samoht/tap/tw │ $ opam install tw.1.1.0 └──── `tw' is an implementation of [Tailwind CSS v4] in pure OCaml. This is the first version I would advise people to use: the previous ones only partially supported some features, and it was unclear where things were breaking. The 1.1.0 release now covers /all/ the Tailwind v4 utilities, variants and official plugins, and more importantly it comes with a proper, mechanical way to check that its output has no visual difference from Tailwind's. `tw' now comes with a CLI that is a drop-in replacement for the Tailwind CLI. It builds whole Tailwind projects from their CSS entrypoint (`tw -i src/app.css -o dist/app.css'), including `@theme', `@source', `@apply', custom utilities and variants, and the typography and forms plugins, *without Node.js*. It works for any project configured in CSS, whatever language generates the HTML. If you are using OCaml you can directly use an OCaml API with [Tw_html] that lets you build HTML components carrying their own styles. It exposes [htmlit]'s API but attaches `tw' classes to every node so you can query what CSS a given HTML fragment needs to be displayed. You can read more about the process to get to this release in my [blog post]. Feedback greatly appreciated in the [issue tracker] or in DMs! [tw] [opam] [Tailwind CSS v4] [Tw_html] [htmlit] [blog post] [issue tracker] Cascade: A Typed CSS Toolkit in OCaml ═════════════════════════════════════ Archive: Continuing this thread, Thomas Gazagnaire announced ─────────────────────────────────────────────────── I am happy to announce the release of `cascade' 1.2.1 in `opam' and Homebrew. It mostly contains (many) bug fixes, a lot of them found by rendering stylesheets in headless Chrome and comparing the pixels. If you shipped minified CSS built with 1.1.0, please regenerate it and check the difference: ┌──── │ $ cascade diff --diff=canonical old.css new.css └──── If you want to check pixel-level differences of the rendering on a given page, you can now use: ┌──── │ $ cascade diff --browser --html page.html a.css b.css └──── This new command helped to identify and fix numerous issues; hence the (way too long) list of fixes in [CHANGES.md]. [CHANGES.md] ppx_deriving_{yaml,ezjsonm,yamlx} 0.5.0 ═══════════════════════════════════════ Archive: Patrick Ferris announced ──────────────────────── Hello! I'm happy to announce the release of `ppx_deriving_yaml' and `ppx_deriving_ezjsonm' version `0.5.0' alongside the first release of `ppx_deriving_yamlx' which uses [YAMLx] as a backend (thank you to @raphael-proust and @mjambon for their work and input). All three derivers use the same core codec with each specialising to their particular representation. This release also contains a bug fix for deriving `_ array' types which was completely broken (so broken, your code wouldn't compile). [YAMLx] Installing ╌╌╌╌╌╌╌╌╌╌ ┌──── │ opam update │ opam install ppx_deriving_yaml │ opam install ppx_deriving_ezjsonm │ opam install ppx_deriving_yamlx └──── Documentation ╌╌╌╌╌╌╌╌╌╌╌╌╌ Please find the documentation on ocaml.org, for example [docs for ppx_deriving_yamlx]. Happy deriving :two_hump_camel: [docs for ppx_deriving_yamlx] kqueue-ml 0.5.0 ═══════════════ Archive: Continuing this thread, Anurag Soni announced ───────────────────────────────────────────── I've published a couple of new releases since 0.5. • Added support for `EVFILT_USER' and `EVFILT_SIGNAL' • Change the interface for registering events to avoid needing to allocate a record The new update can be found on [opam] [opam] Windtrap: one library for all your OCaml tests ══════════════════════════════════════════════ Archive: Thibaut Mattio announced ──────────────────────── Hi everyone! I'm happy to announce the release of [Windtrap], a testing framework for OCaml that brings the different testing methods of the ecosystem into one library: • Unit tests, with an Alcotest-like API. • Expect and snapshot tests, with a companion PPX compatible with `ppx_expect' and `ppx_inline_test', but usable without it. • Property tests as seen in [QCheck], with a library of textbook laws (round trips, orders, associativity, and more). • Stateful and differential tests, as seen in [qcheck-stm] and [Monolith], on one domain or several. • Test coverage (as with [Bisect_ppx]) and mutation testing (as with [mutaml]). Every kind of test composes with the others into one suite and one output, and the library has *no dependencies* beyond OCaml (the optional PPX uses ppxlib). Here's a small suite that tests the standard library with a unit test, a property stated as a textbook law, and a stateful test of `Stack' against a list: ┌──── │ open Windtrap │ │ (* A reference for Stack: a list, its top first. *) │ module Model = struct │ type t = int list ref │ │ let create () = ref [] │ let push x m = m := x :: !m │ let length m = List.length !m │ │ let pop m = │ match !m with │ | [] -> raise Stack.Empty │ | x :: rest -> │ m := rest; │ x │ end │ │ let stack = abstract "s" ~pp:(fun ppf m -> Testable.pp (list int) ppf !m) │ │ let lists = │ group "list" │ [ │ test "rev reverses a list" (fun () -> │ equal (list int) [ 3; 2; 1 ] (List.rev [ 1; 2; 3 ])); │ prop "rev is an involution" (Gen.list Gen.int) │ (Law.involutive (list int) List.rev); │ ] │ │ let stacks = │ group "stack" │ [ │ stateful "behaves like a list" │ [ │ command "create" (Gen.unit @-> makes stack) Model.create Stack.create; │ command "push" │ (Gen.int @-> stack ^-> returns unit) │ Model.push Stack.push; │ command "pop" (stack ^-> returns int) Model.pop Stack.pop; │ command "length" (stack ^-> returns int) Model.length Stack.length; │ ]; │ ] │ │ let () = exit (run "stdlib" [ lists; stacks ]) └──── We built Windtrap at [Invariant] as part of our work on making code written by agents more trustworthy. Agents replicate what they find in a repository, so a project full of strong tests gets more of them. Windtrap comes with an [agent skill] that guides agents towards the right kind of test, but we care just as much about humans, and put a lot of effort into making the API and output ergonomic and delightful. You can read more about it in the [announcement post]. It walks through an example suite, what a failure looks like, coverage and mutation testing, and an experiment where we used Windtrap to test the OCaml standard library. To install it (when [the PR] is merged on opam-repository): ┌──── │ opam install windtrap ppx_windtrap └──── The [manual] (written by agents with my guidance, I hope you'll find the writing voice bearable, but if enough people complain I'll look into rewriting it by hand) has one page per kind of test. Feedback is very welcome, here or on the [issue tracker]. Happy hacking! — [Windtrap] [QCheck] [qcheck-stm] [Monolith] [Bisect_ppx] [mutaml] [Invariant] [agent skill] [announcement post] [the PR] [manual] [issue tracker] Acknowledgments ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ Windtrap builds on ideas and code from several OCaml projects: • *[Alcotest]* by Thomas Gazagnaire: test structure and runner design. • *Craig Ferguson's Alcotest PRs* ([#294], [#247]): API design, subcomponent diffing. • *[QCheck2]* by Simon Cruanes et al.: generator design and integrated shrinking. • *[Monolith]* by François Pottier, and *[qcheck-stm]* by Jan Midtgaard et al.: stateful testing against a reference, on one domain or several. • *[ppx_expect]* and *[ppx_inline_test]* by Jane Street: expect test paradigm and dune integration, and ppx_expect's test corpus. • *[Bisect_ppx]* by Anton Bachin et al.: coverage instrumentation and runtime. • *[mutaml]* by Jan Midtgaard: mutation testing for OCaml as a PPX. • *[mtime]* by Daniel Bünzli: the monotonic clock. [Alcotest] [#294] [#247] [QCheck2] [Monolith] [qcheck-stm] [ppx_expect] [ppx_inline_test] [Bisect_ppx] [mutaml] [mtime] Ocsigen Server 8.0.0 ════════════════════ Archive: Vincent Balat announced ─────────────────────── Hi everyone! Ocsigen Server 8.0.0 is out, the web server written in OCaml, usable as an executable or as a library. Some highlights: • *One command, no config file*: `ocsigenserver ./public' serves a directory, and `ocsigenserver --reverse-proxy http://localhost:9000' puts a proxy in front of your app, with compression and security headers already on. • *Static files* now answer conditional requests (ETag, 304) and byte ranges, so seeking in a video or resuming a download just works. • *zstd compression*, on a new bytesrw backend. • A new *Securityheaders* extension. • Access logs in the standard *Combined Log Format*, readable by the usual tools. • The public modules were renamed (`Ocsigen.Server', `Ocsigen.Config', …). The new `ocsigenserver-compat' package keeps the old names, so existing code still builds. `opam install ocsigenserver' A release of Eliom and other projects compatible with this version will follow in a few weeks. All the details are in the blog post: Old CWN ═══════ If you happen to miss a CWN, you can [send me a message] and I'll mail it to you, or go take a look at [the archive] or the [RSS feed of the archives]. If you also wish to receive it every week by mail, you may subscribe to the [caml-list]. [Alan Schmitt] [send me a message] [the archive] [RSS feed of the archives] [caml-list] [Alan Schmitt]