How to use WASM code from ClojureScript through JavaScript interop


Clojure is a great programming language. I have been working with it lately, and it’s been a blast. Another language that I love is Rust. One day I was thinking about both languages, their advantages and disadvantages, and under which circumstances I should use each. That made me wonder if it would be hard to use WASM from Clojurescript.

My idea was to:

  1. Create a simple function in Rust
  2. Use wasm-bindgen crate to generate the WASM code and JavaScript bindings.
  3. Load the code into JavaScript
  4. Call WASM code from ClojureScript through the JavaScript interops

You can check the final code at https://github.com/danielorihuela/wasm-on-clojurescript-through-js-interop.

The first two steps were easy. I created a simple Rust function.

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(a: u8, b: u8) -> u8 {
    a + b
}

Then, I built the WASM code and JavaScript bindings with the following command:

wasm-pack build --target web

Now is when the interesting part starts. We want to use the generated JavaScript bindings to call the WASM code. First, we need to create a simple website and load the code in the browser. In our case, we want to access the WASM functions from the “windows” object. So I ended up with the following piece of code.

<script type="module">
  import init, { add } from './wasm/addition.js'

  async function run() {
    await init();
    window.addWasm = add;
  }

  await run();
</script>

The WASM is now loaded. We are ready to execute the “hello world” website.

clj -M --main cljs.main --compile hello-world.core --repl

But there’s one problem.

The wasm file cannot be validated. I suspect it is related to Will Cohen’s comment on https://clojure.atlassian.net/browse/CLJS-3387. So I tried to serve it with miniserve. It worked!

We can call WASM code with with the following approach: The cheatsheet tells us how to access the function we jsut exported.

(. js/window (addWasm 5 10))

You can check the final code at https://github.com/danielorihuela/wasm-on-clojurescript-through-js-interop.

And this is how we can call WASM from ClojureScript from a basic project. The problem with that approach is that we lost the REPL since we cannot use the ClojureScript browser. However, you will probably use a build tool like shadow-cljs. An approach exists for shadow-cljs to use WASM with the REPL. You can check it https://github.com/thheller/wasm-pack-cljs.