WebRacket is a compiler that translates a substantial subset of the Racket programming language into WebAssembly, enabling developers to build web applications without JavaScript while leveraging Racket's expressive power.
The WebRacket project introduces a direct path for Racket programmers to target the web browser, compiling a substantial subset of the language to WebAssembly (WASM). This approach sidesteps the complexities of JavaScript toolchains while offering the familiar expressiveness of Racket. The long-term vision is full Racket compatibility, but the current implementation is already robust enough for practical web development, as demonstrated by its inclusion of bindings for DOM, Canvas, MathJax, XTermJS, and JSXGraph. The project's focus is squarely on the browser, though it can also execute in Node.js for terminal-based testing.

Supported Language Subset
The compiler accepts a top-level form, though the module form is notably absent; include is available as a substitute. The supported data types cover most basic structures, but with specific limitations:
- Numbers: The numerical tower is restricted to flonums and fixnums. Complex numbers and bignums are missing, though the author notes these are likely to be implemented sooner rather than later due to personal interest.
- Hash Tables: Mutable hash tables of all four varieties (eq?, eqv?, equal?, always?) are supported. However, immutable hash tables are not yet supported, and mutable tables hold values strongly, even those created with weak constructors.
- Regular Expressions: No direct support exists at the moment, pending improvements to linklets and modules.
- Ports: Given the browser target, only string (and byte string) ports are supported. File ports are available only if there is community interest.
- Structures: Most structure features are implemented, including super structures, structure properties, and applicable structures. Prefab structures are the most notable omission.
Syntactically, WebRacket leverages the standard Racket expander, supporting a large number of forms including for and match. The primary omissions are module, module*, and with-continuation-mark. Control flow is well-supported: tail calls, multiple values, and upward-flowing exceptions work. However, continuations and continuation marks are not supported, meaning call/cc is absent. Other missing features include promises, breaks, exit, and black box. The system is single-threaded, and concurrency/parallelism features are not yet available.
JavaScript FFI and Bindings
A critical component is the JavaScript Foreign Function Interface (FFI), which allows WebRacket programs to access browser functionality and standard JavaScript libraries. The FFI enables WebRacket functions to serve as event callbacks on the JavaScript side. The project includes pre-written bindings for several popular libraries:
- Math: Standard JavaScript Math object.
- DOM: For manipulating the Document Object Model.
- Canvas: For 2D drawing operations.
- MathJax: For rendering mathematical formulas (specifically MathJax 4).
- XTermJS: For terminal emulation in the browser.
- JSXGraph: For interactive geometry and data visualization.
The hope is that the community will contribute bindings for other commonly used libraries. The author also suggests that generating these FFI bindings could be partially automated using LLMs.

Compiler Architecture and Implementation
The WebRacket compiler is a direct-style compiler, a design choice that simplifies relating the generated WebAssembly back to the source program. The compilation pipeline proceeds through several stages:
- Frontend: Uses
read-syntaxto parse a WebRacket file into a syntax object, which is then fed into the standard Racket expander to produce a fully expanded program. - Middle End: A series of passes implemented using the NanoPass framework. These passes perform transformations such as unexpanding, parsing, flattening top-level begins, inferring names, converting quotations, explicit begins and case-lambdas, α-renaming, assignment conversion, categorizing applications, anormalization, closure conversion, and flattening begins.
- Backend: The code generator produces WebAssembly in S-expression format (folded). This generator is inspired by "Destination-driven Code Generation" by Dybvig, Hieb, and Butler, though it generates nested WebAssembly instructions rather than flat assembler code. The final step uses the external
wasm-toolsto convert the S-expressions into bytecode.
The runtime is split: the WebAssembly runtime is in runtime-wasm.rkt, while the JavaScript support is in assembler.rkt. A design goal is to minimize reliance on the host environment, though interfacing with the JavaScript host is unavoidable for browser functionality.
Examples and Practical Usage
The examples/ directory showcases WebRacket's capabilities. Running these requires a local web server, for which raco-static-web is recommended.
- MathJax 4: A two-pane editor where LaTeX input is rendered in real-time. It demonstrates accessing the
MathJaxobject viajs-varandjs-send, and invoking methods liketypesetPromise. - Matrix Rain: A digital rain effect using XTermJS bindings for terminal emulation and browser callbacks for animation.
- MiniScheme: A Scheme REPL implemented in WebRacket, running inside an XTermJS terminal. It includes a custom interpreter and editor interface.
- Pict: A port of Racket's picture library, demonstrating graphical capabilities.
- Space Invaders: A full game using Canvas for rendering, mutable structs for state, keyboard event handling, and
requestAnimationFramefor the game loop. - XTermJS Demo: A recreation of the XTermJS homepage demo, showing DOM manipulation and terminal control.

Installation and Dependencies
To use WebRacket, you need:
- wasm-tools (version 1.243.0 or newer) from the Bytecode Alliance for compiling
.watto.wasm. - Node.js (recent version supporting
--experimental-wasm-exnref) for terminal execution. - Racket 9.0 or newer.
- raco-static-web for local testing.
Installation involves downloading wasm-tools, ensuring Node.js is in your PATH, and installing the static web server via raco pkg install raco-static-web.

The Road Ahead
Future development priorities include fixing bugs from early adopters, then focusing on module support (which requires linklets). Complex numbers and bignums are planned. Impersonators and chaperones are needed for contracts. Full regular expression support depends on unlocking modules. Continuations and continuation marks are a high priority but are tricky; a CPS pass may be added to the compiler as a last resort.

Conclusion
WebRacket represents a significant effort to bring Racket's powerful language features to the web platform. By compiling to WebAssembly and providing a robust FFI, it allows developers to write sophisticated browser applications using a language known for its metaprogramming and DSL capabilities. The project is currently in active development, with a clear roadmap and a growing set of examples that demonstrate its potential for building everything from games to interactive educational tools.

Comments
Please log in or register to join the discussion