# raster compiles typed Clojure to WebAssembly for the browser but keeps its multiple dispatch on the JVM

> A numerical computing library for Clojure built on typed multiple dispatch, parallel combinators and a self-owned nanopass compiler with no LLVM dependency. The interesting boundary is where the fast path stops: kernels cross to the browser as WebAssembly, dispatch and redefinition do not.

**replikativ/raster** — Fast, functional numerical computing for Clojure/JVM.

- Repository: https://github.com/replikativ/raster
- Stars: 67 · Forks: 3
- Language: Clojure
- License: MIT
- Published: 2026-08-27 · Updated: 2026-08-27 · Language: en
- Canonical page: https://hysenlabs.com/projects/replikativ-raster

## deftm is a macro, so typed functions stay ordinary values

The central claim is that typed numerical functions in this library are not a separate language. deftm is a macro, so what you define is a regular Clojure value that still works at the REPL and still composes with everything else in the language. The example in the documentation is a two-argument norm function taking typed parameters and returning a typed result, and calling it with three and four yields five. What the macro adds is compilation to primitive JVM bytecode with no boxing, which is the difference between a numeric loop and a numeric loop that allocates on every iteration. Multiple dispatch is what lets one name have several definitions: the most specific method is chosen automatically, with the compiler resolving the choice at the call site when argument types are known, either by picking the closest concrete method or by specializing a parametric template, then emitting bytecode with no dispatch overhead at run time. The whole example fits on a screen:

```clojure
(require '[raster.core :refer [deftm ftm]])
(require '[raster.numeric :refer [+ *]])
(require '[raster.math :refer [sqrt]])

;; Typed function — compiles to primitive JVM bytecode, no boxing
(deftm norm [x :- Double, y :- Double] :- Double
  (sqrt (+ (* x x) (* y y))))

(norm 3.0 4.0) ;; => 5.0
```

The type annotations sit inside the argument vector rather than in a separate declaration, which is part of why the definition still reads as ordinary Clojure to anyone not looking at the compiler.

## Parallel combinators are compiler nodes, not library functions

Data parallelism is expressed with map, reduce and scan variants that read differently from their sequential counterparts, and the reason they perform is that they are not implemented as functions over collections. The compiler treats them as first-class intermediate representation nodes, fuses producer and consumer chains so an intermediate array is never materialised, and lowers the result to SIMD loops, OpenCL kernels or Vulkan compute from the same source. That last clause is the significant one: the backend is a compilation target rather than a deployment choice, so moving a computation from the processor to a GPU is a change of target rather than a rewrite. The inspiration is named in the documentation as Futhark, and the approach is a consequence of owning the compiler rather than of adding an optimisation pass at the library level.

## The compiler compiles from Clojure to bytecode without LLVM

The compiler is described as a transparent nanopass system that you own and can inspect, and the detail that matters is the negative one: compilation goes from Clojure source directly to JVM bytecode, with no LLVM dependency. That removes an external toolchain from the build and gives control over every optimization pass, which in turn is what makes multiple backends reachable: JVM bytecode with SIMD vectorization, OpenCL C, Vulkan SPIR-V, or Intel Level Zero. C output is possible, and LLVM IR potentially, but the default path avoids external toolchain dependencies entirely. The pipeline is inspectable through a dedicated function rather than only through debug output, so you can see what a definition compiles to. The stated ambition is to reach deep-learning performance on the level of JAX while staying a normal library.

## The browser gets the kernels, not the program

The cross-platform story has an explicit boundary and the documentation is careful about which side of it things fall. The typed functions and value types compile to primitive bytecode ahead of time on the desktop and to a 64-bit floating point WebAssembly module in the browser, so terrain noise, biome generation and the physics kernels are written once. Value types stay unboxed and marshal across the WebAssembly boundary as flat structs. Everything else, meaning the game loop, streaming and meshing, is ordinary portable code that happens to be JavaScript in one target and bytecode in the other. The reason this split works is that the WebAssembly target is a numeric subset consisting of flat memory, value types and control flow, compiled ahead of time. What crosses is the kernel set rather than the whole program, which is a constraint to design against rather than a limitation to work around.

## Runtime dispatch and REPL redefinition stay on the JVM

Two capabilities do not cross to the browser at all, and the documentation names both without hedging. Runtime multiple dispatch on typed functions and redefinition at the REPL are JVM-only, because the target language has neither. That is a real constraint on how you structure a library that must serve both: anything whose behaviour depends on choosing between definitions at run time, or on redefining a function while the program is live, stays on the desktop side. It also means the interactive workflow that motivates the library is unavailable in the browser build, which is a fair trade for compilation but should be planned for rather than discovered. The documentation argues that for a voxel game the boundary lands naturally, because terrain and physics are the hot numerics and everything else is glue.

## One shader description emits both GLSL and WGSL

Rendering is handled the same way the numerics are, with a single small renderer protocol that both games draw against and one shader description that emits both shader languages: GLSL for Vulkan on the desktop and WGSL for WebGPU in the browser. One renderer, two graphics APIs, is the summary. The paired examples are described as demonstrating typed dispatch, parallel primitives and procedural generation, and the voxel example is more than a tech demo: it streams a world with biome terrain, caves, ore veins, trees, a day and night sky, skylight alongside block lighting from a glowstone source, water, passive mobs and mining. The shape example extends polygon types from the REPL during play, which is the feature that would be impossible if the typed definitions were baked at compile time on that target.

## Eight notebooks cover the surface, and releases ship several times a day

The learning path is eight interactive notebooks, and their names tell you what the library considers its core. The first covers typed functions, dispatch and value types; the second covers forward-mode and reverse-mode automatic differentiation with combined value and gradient results; then ODE solvers demonstrated on the Lorenz attractor with adaptive stepping and events, linear algebra covering vector and matrix types with LU, Cholesky and SVD, optimization with L-BFGS, Nelder-Mead and Newton's method, deep learning as a multilayer perceptron trained with compiled differentiation, agent-based modelling of endogenous firm formation compiled to the GPU, and geometric algebra with rotors and precession. Against that breadth, the release cadence is the thing to plan around. Three consecutive releases are numbered 1134, 1135 and 1136, all on the same day, on a 0.2 line, so pinning is the only sensible default.

## Conclusion

Use raster if you want numerical code in Clojure that compiles to primitive bytecode without leaving the REPL, since deftm is a macro over ordinary Clojure values rather than a separate DSL, and the compiler avoids an LLVM toolchain dependency. Do not expect the browser target to be the same experience: multiple dispatch at runtime and REPL redefinition stay JVM-only, and only the numeric subset crosses to WebAssembly. Before you commit, read the split between kernels and orchestration, because it decides which of your code needs rewriting for the web. And note this is a 0.2 release line publishing several builds a day, so pin the version.

## FAQ

### What does the word raster mean?

The library is a Clojure numeric computing project and does not use the word in its usual graphics sense. It provides typed functions, parallel combinators and automatic differentiation, compiling to JVM bytecode and to WebAssembly for the browser.

### What is another term for raster?

In graphics the usual synonym is bitmap, but this repository does not discuss the word at all. Its own naming starts from typed multiple dispatch over primitive arrays, inspired by Julia, and parallel combinators inspired by Futhark.

### What is the difference between a raster and a vector?

The repository does not cover that distinction. It covers numerical computing in Clojure, and its one rendering layer is a shader description that emits GLSL for Vulkan and WGSL for WebGPU from a single source.

### What is raster in gaming?

The repository does not use the graphics term. Its demos draw through one renderer that emits GLSL for Vulkan and WGSL for WebGPU from a single shader description, so the distinction it draws is between those two shader targets, not between raster and vector graphics.

## Sources

- [Official README](https://github.com/replikativ/raster#readme)
- [Project repository](https://github.com/replikativ/raster)
- [Release notes](https://github.com/replikativ/raster/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/replikativ-raster
