Hazel reports · ^report(34) · Hazel as a JS library · 2026-10-26 · work in progress · after ^report(33)

An Official Hazel JS Library?

Since 2026-10-20 a game in daily use, Messed Up Dress Up (MUDU), runs its rules in Hazel through a draft library, hazel-lib.js (#2711, with #2719 stacked on it). Should Hazel make such a library official: a supported way for any web page to analyze, run and show Hazel programs? This report lays out what the draft does, what the game has taught us, the uses beyond it, and the costs.

Short answers.

  1. What it is today: one script, 643 KB over the wire (2.7 MB unpacked; the full app is 1.7 MB), that puts window.Hazel on a page: analyze and run a text, analyzeProgram and runProgram for a program of named modules, with Hazel values coming back as plain JS values and JS functions going in and out both ways. Plus an embed mode: Hazel's own tiles in an <iframe>, showing (and, on a further branch, editing) a program the page posts.

  2. For: it already works for a real, typed, event-driven use; it is small to keep (one OCaml module and a doc over the existing evaluator); and it opens Hazel to every page that wants live, typed, hole-tolerant code: lessons, docs, games, other editors.

  3. Against: an API is a promise. It fixes how Hazel values look in JS, ties pages to builds that today are branch URLs with no versions, runs on the page's own thread, and puts an evaluator in pages that may run code they did not write.

  4. Suggestion: make it official in two steps: first merge #2711 and #2719 as an experimental library (labelled so, built on dev), then promise stability only for the small core (analyze, run, the value mapping) once a second user besides MUDU has used it.

Update, 2026-11-02: a first npm release, hazeljs 0.0.1, built from #2719 at 0d66c36618. For Hazel's core team: beyond dev it carries only the library's own 11 commits, from #2711 and #2719. It has no modular editors (#2469), no livelits, and none of the performance PRs still open against dev. Three of those commits touch Hazel's core, and the release is 24 PRs behind today's dev (below).

What the draft library does#

CallWhat it gives
Hazel.analyze(text){errors}: the program's error messages.
Hazel.run(text){errors, value}. Errors do not stop a run: Hazel evaluates around its holes, so the page decides which errors it accepts.
Hazel.analyzeProgram(spec), runProgram(spec)A program from named modules' texts (one editor each), a preamble and a tail; errors, holes and unfinished brackets per module; ok when there are none.
Valuesnumbers, booleans, strings; lists as arrays; () as null; labeled tuples as objects; constructors as {tag, value}; functions as JS functions, called with converted arguments.
Embed mode (index.html#embed)Hazel's own tiles and error marks for a program the page posts, read-only on #2719; selectable and editable, with themes, fonts, line numbers and go-to-definition, on the embed-editable branch (12 commits, no PR yet).

Sources: docs/hazel-as-a-js-library.md on #2719's branch; src/web/HazelLibCore.re; 17 test cases (HazelLib), under node with the rest of the suite. Sizes measured 2026-10-26 from hazel.org's branch builds.

What the game has shown#

MUDU's 📜 Scripting panel gives five kinds of room a rule each: a function from the game's event (a lever pulled, a way taken) to a tree of commands the game carries out (^report(33) describes the rules and their types). The same rules can be written in Hazel (the default), in JS, or in Fumola, which makes a fair comparison.

Try it: the trap room, on this page#

Added 2026-11-03, a first version of the live trap room (^ticket(675)). This is the game itself, Messed Up Dress Up 0.0.475 or later, opened in the castle's trap room with its 📜 Scripting tab showing (the link ?try=trap, ^ticket(676)). Its Hazel comes from the npm package, hazeljs 0.0.1 (since 0.0.476, ^ticket(674)). Pull a red lever, or walk onto a pit, and the room's Hazel rule, TrapRoom's on_event, says what happens; edit the rule in the tab, and the next pull answers the edited rule. Nothing done here is saved: a game you play on this site is as you left it.

The game in a frame. Open it full size. The next version puts Hazel's tile editor and the room side by side on this page, wired through the package (^ticket(677), ^ticket(678)).

Uses beyond the game#

UseWhat the library gives it
Teaching pages and coursesExercises checked in the student's page: analyze for type errors, run for tests, without hosting the whole Hazel app.
Docs and papers with live examplesAn example's value computed from its text where it is shown, so it cannot go stale; the embed frame to show it as tiles.
Other games and toysMUDU's pattern, rules as typed functions from events to commands, for any game that wants players to script it.
Other editorsA notebook, a slide tool or a web IDE with Hazel cells: the embed frame for the tiles, the library for the values.
Livelits in other pagesA livelit's model and view as Hazel values a host page can drive (today they live only inside Hazel).
Testing and toolingScripts that run Hazel programs under node (the library's own tests already do), for checks in other projects' CI.

For making it official#

Against, or costs to plan for#

Update, 2026-11-02: the npm release, and what it is built on#

A first, experimental release of the library is packaged for npm as hazeljs 0.0.1: hazel-lib.js as one file, entry points for Node's require and import and for a page's <script> (from a CDN that serves npm packages), TypeScript declarations, and a README. Its README sends readers to hazel.org, and to the contributors on GitHub, for who makes Hazel. The name is Matthew's choice. Here, for Hazel's core team, is exactly what the release contains.

Built from dev of 2026-10-19 and two PRs, nothing else#

The library file is built from hazelgrove/hazel at 0d66c36618, the head of #2719 (branch js-library-embed), with GITHUB_SHA set so that Hazel.build names that commit. That commit is dev at 962a7cb009 (2026-10-19) plus 11 commits, and no merges, so no other branch is in it:

FromCommitsWhat they change
#2711, the library (js-library, head 77059807e9)6New files only: src/web/HazelLib.re and HazelLibCore.re (the API and the value mapping), docs/hazel-as-a-js-library.md, test/Test_HazelLib.re, and their dune rules. Nothing else in the app calls them.
#2719, programs of modules and embed mode (js-library-embed, head 0d66c36618)5The library's analyzeProgram and runProgram; embed mode, a new mode of the app entered only from index.html#embed (src/web/embed/, two lines in Main.re, embed.css); and the core changes below.

What touches Hazel's core (src/haz3lcore, 830 lines added and 31 removed, all in #2719's commits 61d8bf6f2b, 4538863089 and 0d66c36618):

Not in it#

Behind today's dev#

The release is 151 commits, 24 PRs, behind dev as of 90d1005c9e (2026-10-29). Those that change what a program means or how it reads: curried function definitions (#2403), folded constructor heads in pattern matches (#2391), and streaming evaluation in read-only cells (#2683). Those that will matter for merging the library: the .rei interfaces for src/util and leaf modules (#2439, #2440), which the library's new uses must be declared in, and the evaluation budgets in CI (#2678). A trial merge of 0d66c36618 into today's dev conflicts in one file, src/web/dune (the modules lists); whether it builds against the new interfaces is not checked yet.

Found while packaging#

Both are in the README's limits.

What next#

  1. Ready #2711: move the stranded constructor fix onto it, bring its description up to date (the editor in a page moved to #2719 and beyond), and take it out of draft.
  2. Open a PR for the embed frame's 12 commits (embed-editable), stacked on #2719, so the whole of what MUDU uses is under review.
  3. Decide the #2469 question (outline and navigation from modular editors) before the library grows its own.
  4. Convert JS values directly into Hazel values at the call boundary, not through syntax re-evaluated (^report(25)'s floor), and merge #2681, which the game's typed rules need.
  5. Run analysis in a worker in the library, as the app does, so a slow read never freezes a page.
  6. Version it: a build per release at a stable URL, Hazel.version a real number, and the stable core named in the doc.
  7. Find a second user (a course page, or the docs' own examples) before promising stability beyond the core.

How this was put together#

From the branches' code and docs (#2711, #2719, embed-editable), ^report(25)'s measurements of a call, MUDU's own use of them (its 25-scripting.js and event log), and sizes fetched from hazel.org's branch builds on 2026-10-26. The timing figures are the doc's own, measured under node on a laptop; nothing new was measured for this draft.

Updates#

2026-10-26: first draft, from ^ticket(635), for Matthew to read before it goes out.

2026-11-02: the npm release and what it is built on (above), for Hazel's core team (Matthew's ask, ^ticket(668)).