[ANN] (Experimental) WasmTarget.jl & Therapy.jl: Julia-to-WebAssembly compiler, with a full-stack signals framework built on it

[ANN] (Experimental) WasmTarget.jl & Therapy.jl: Julia-to-WebAssembly compiler, with a full-stack signals framework built on it

I’m “announcing” two new packages, both registered in General. They might be usable, might not. The main goal is feedback from actual subject-matter experts, and to put these in front of anyone who wants to play around with them, like I do.

WasmTarget.jl: compiles real Julia functions to WebAssembly. It reads Julia’s fully-inferred IR (Base.code_typed()) and emits WasmGC bytecode directly. Targets modern WasmGC types (struct, array, externref) so Vector{T} and user structs map naturally instead of fighting linear memory.

  • Current scope: 176 Julia functions compile and produce correct E2E results across Int32 / Int64 / UInt32 / UInt64 / Float32 / Float64. 127 are native paths (real Base IR), and 48 are overlay reimplementations for IR tangled with GC internals or libm foreigncalls. This is the same pattern CUDA.jl / GPUCompiler.jl uses. 2409 tests passing. Optional wasm-opt pass yields ~85% size reduction with no behavioral regressions.
using WasmTarget: compile_multi

square(x::Float64)::Float64 = x * x
cube(x::Float64)::Float64   = x * square(x)

# Both functions in one WASM module. `cube` calls `square` inside the binary.
bytes = compile_multi([(square, (Float64,)), (cube, (Float64,))])
write("cubic.wasm", bytes)

Therapy.jl: a full-stack web framework with fine-grained signals (in the SolidJS / Leptos sense) and Astro-inspired islands architecture. Components are written in a JSX-style call syntax like Div(Button(...), Span(...)), but it’s plain Julia function composition, no macro or template DSL.

  • Islands: most of the page is static HTML, and only the parts that actually need reactivity ship JS / WASM. With Therapy each @island function compiles to its own per-island WASM module via WasmTarget. You write SSR layouts and reactive components in plain Julia; the build emits static HTML plus tiny WASM modules (1 to 12 KB per island) that hydrate on demand.
using Therapy: Div, Button, Span
using Therapy: @island, create_signal, create_memo, create_effect, js

@island function InteractiveCounter(; initial::Int = 0)
    count, set_count = create_signal(initial)
    doubled = create_memo(() -> count() * 2)
    create_effect(() -> js("console.log('count:', $1, 'doubled:', $2)", count(), doubled()))

    return Div(
        Div(
            Button(:on_click => () -> set_count(count() - 1), "-"),
            Span(count),
            Button(:on_click => () -> set_count(count() + 1), "+")
        ),
        Span("doubled ", Span(doubled))
    )
end

Both docs sites dogfood Therapy itself:

Both repos were built iteratively with LLM coding agents, so if you’ve shipped real compilers or built real web frameworks and have any interest in a real Julia to Wasm story, I’d love any and all feedback from those of you with much more domain knowledge than me.

Repos:

Neat!

Is this related to the previous (I think abandoned?) work some people did on WebAssemblyCompiler ? The ability to show DiffEq and (modified) Makie running in the browser always made for impressive demo material for Julia presentations.

Not really. That project was before WasmGC and didn’t take advantage of any of the garbage collection features now native to Wasm

It looks great as it clearly works , but it might feel more comfortable if it is (more) hand-written with carefully crafted details.
Some compiler tasks nowadays might work well with the generative AI, especially for some X-to-Y transpiler when there is at least one existing de facto approach to execute X and collect results. However, if a human cannot control details, should we use some mechanisms to make AI-powered compiler work more reliably, like domain modeling-based tests (split things into orthogonal and complete bases/cases and test against every basis so any composition works)?

A lot of projects only exist because it is a lot of work to do it by hand, and nobody is willing to spend the time and effort do so. I think if nobody wants to do it by hand, then AI is the second best thing.

edit: removed poorly worded comment

Lol, besides being just a generally weak argument against someone giving constructive feedback, it’s particularly funny that you should say that to @thautwarm of all people. You should take a look at Taine’s history before saying that.

It wasn’t a personal comment, it was a comment about putting in the work to bring julia → webassembly. I mean that others could have done it, but it never happened. Because it is a lot of work

I absolutely agree with you which is why I am seeking feedback from people who actually know a bit about Wasm or the Julia compiler. I am hoping that I can help in various ways but I genuinely don’t have enough compiler knowledge to do much on my own without agents

@thautwarm Also, if you have any libraries in the Julia world or adjacent to it, that you could point me to which demonstrate the testing design you are talking about, I would love to look more into this!

A Quick Progress Update (Still Very Much Experimental!)

It’s been a few months since the original post, and with a lot of help from Claude, WasmTarget has come a surprisingly long way. A few fun highlights:

  • Automated Testing: The compiler kind of tests itself now. There’s a differential fuzzer (thanks to the great Supposition.jl package) that makes up random Julia programs, runs each one both natively and as compiled WebAssembly via WT.jl, and flags any difference: the return value, whether it threw, or even how it mutated its arguments. Instead of me hand-maintaining a “what works” list, correctness gets stress-tested on every run. The rule it follows is correct-or-loud: if something can’t be compiled faithfully, it errors clearly instead of quietly giving a wrong answer.
  • Version Support: Julia 1.13 works now, alongside 1.12 (CI is green on both).
  • Standard Libraries: Starting to pull in standard libraries. Statistics (mean/median/quantile…), Dates, and Random now compile and match native results. The nice surprise was how much of this “just compiled” once it started collecting the whole reachable call graph the way juliac --trim does upstream, rather than me whitelisting functions by hand.
  • Registration: It’s registered now, so it’s just Pkg.add("WasmTarget").

I am still not a compiler person and still don’t really know what I am doing. A huge amount of the hard stuff has been driven by agents and I’m learning as I go, so I’m very open to feedback (especially on testing and soundness).

The Fun Part

It’s real enough now that actual Pluto notebooks run as interactive WebAssembly “islands,” fully in the browser with no Julia server. Image processing, 2-D convolution, Mandelbrot/Julia fractals, dithering, and Newton’s method are all recomputing live as you drag the sliders:

Live gallery: https://grouptherapyorg.github.io/PlutoIslands.jl/


(The plots are drawn by a tiny Makie-style library, WasmMakie.jl, that also compiles through WasmTarget.) I’ll do proper write-ups for PlutoIslands.jl and WasmMakie.jl down the line. I just wanted to share where things are heading.

If I am not mistaken, this allows to run simulations in the user browser by only serving a “static” website.

How is it related to bonito? I have a GUI made in Bonito (which relies on SCiML), is it “easy” to use Therapy.jl to serve the GUI as page ?

Very exciting progress, congrats!!

I’m also working on some Wasm support! Will definitely try to integrate anything working nicely into Bonito :wink:

@rveltz that is a great question and considering SciML stuff is the shining star of the julia ecosystem it is on my list to get WasmTarget.jl working with SciML. That being said, I am not sure when that will land, and when it does land it will probably be incremental (e.g. supporting one small library within SciML at a time). I will make sure to update this thread as that occurs!

Also, I haven’t actually tried hosting a Bonito GUI inside a Therapy.jl webapp. I will try that soon and if it works, I will add it to the Therapy.jl docs!

@fonsp thanks so much!

@sdanisch Are you working on a related compiler for Julia->WASM? I’d love to see your approach or talk through ways that WasmTarget.jl might better integrate with Bonito/BonitoBook if that’s of interest at all! Also this might be a nice place to discuss more: WasmGC strategy · Issue #62087 · JuliaLang/julia · GitHub

Hi everyone,

I’ve been following the updates on WasmTarget.jl almost daily and sharing tweets on Twitter, and it’s being steadily developed. The progress a single developer has made using AI tools is absolutely incredible. For a project this massive and automation-heavy, I truly believe using AI was the right choice.

We should test this project more, provide feedback, and support it. In my opinion, it’s well ahead of similar initiatives. This might even be as crucial for the ecosystem as Julia generating standalone executables and dynamic libraries. Bringing Julia’s functionality to the browser will pave the way for educational tools, single-page apps (SPAs), and serverless online calculators.

As soon as the standard library reaches a reasonable level of readiness for Wasm, I plan to port some of my own packages to the browser. This way, people will be able to interact with our Julia code without the hassle of installing anything.

For a while now, I’ve been developing online educational tools using Scala.js to assist with my lectures. The day I can successfully run my Julia code in the browser, I won’t need that anymore. For my workflow, HTML + JavaScript + Julia would be a much better alternative on the web than any other option.

Just wanted to share my excitement and thoughts with the community!

Thanks for the encouragement! I agree, I think Wasm can be huge for Julia as a whole! AI is pretty incredible at what it can do when it has a good spec (the WasmGC spec itself) and an oracle to test against (the Julia compiler itself).

I am working on actual SciML libraries now, step by step, but the first little improvement landed and you can play with it on the docs now: Examples - WasmTarget.jl

Also, I know ForwardDiff.jl is not technically SciML