I think we should add a CI test that builds the compiler for a WASM target -- to ensure we maintain this as an option for embedding roc in future.
Currently we can't build for WASM due to using realpath, but it's only in a few places -- in coordinate I think.
I've somewhat isolated those using a (very average) abstraction, but it would be good to sort this out now because it might get painful later to change.
Is this something anyone would like to look at?
Wasm in this case should be wasm32-freestanding-none
?
Or are we targetting wasm32-wasi-musl
and something like wasmer js sdk
I'm not sure. For embedding the roc compiler in WASM -- we would need a way to provide things like files etc.
I guess we could compile to WASI...
My concern is that we add more features over time and it becomes harder to embed the compiler in a WASM context.
I'm not sure we need to fully abstract things like files now, but we at least want to isolate those somehow.
If the goal is the web repl, we don't need files at all.
Yeah, I can imagine things like REPL or interpreter running in WASM
Though I guess it might be nice to import from github via the web repl
Or even the formatter, or eventual LSP maybe.
Anyway, I think we have to work via wasm32-freestanding-none
if we want to run in the browser with no dependencies. In that case, roc won't be an executable at all. It will be a library and js will have to fill in filesystem hooks.
Yeah, not sure how we do that. Or would that just be libroc?
Yeah, we probably want to make a root.zig
and a library form of roc.
Looks likes all issues today are around filesystems and time measurement
Oh, though timing is for the cli, so it won't be in the library at all
So yeah, I think it is just either making the library level not interact with the filesystem (assume all data is already loaded), or make it abstract away the filesystem (likely nicer and more flexible).
So shouldn't be too bad
Yeah, it was the filesystem and also specifically our use of "paths" that I'm concerned about
I guess porting the formatter to the filesystem api, making a dummy filesystem for wasm (can be totally unimplemented), and adding wasm to ci, would at least stop any new problematic code from being created.
Yeah something like that was what I was thinking.
Though I'm not sure what the right api to cut for wasm is. Like can the current api reasonably be implemented in a browser?
The API for the (internal) compiler functionality?
Like load and typecheck this roc module? or format this file?
The file api. Cause we expect that it will be implemented in the browser by js
We might want to seriously consider getting roc check
and roc format
working in the browser now. That likely will help with a lot of pain that would come later.
I was literally just thinking about posting this also
I was thinking about moving our Filesystem abstraction into a toplevel file called io.zig
and isolating all IO must go through that.
And to prove to myself it works, I was going to make a WASM build of the compiler
I was thinking WASI for ease of use, but WASM should be doable also
yeah WASM seems best
e.g. for roc repl
and the playground
WASI feels like a leaky abstraction we shouldn't need to depend on
It's easy to build a WASI binary though and share a .wasm
file that anyone could run using a WASI runner like wasmer etc
I was just thinking for validating the implementation, not adding a new compile target or setting up anything in CI
Though... I wonder how hard it would be to parse S-expressions in JS land ...
not adding a new compile target or setting up anything in CI
We definitely should setup a build once it works so it doesn't regress.
And yeah, wasmer as a starter target so you don't have to mock all the api's sounds reasonable, but doesn't wasi just give you standard io primitives, so it won't guarantee all io goes through io.zig?
It doesn't guarantee that everything foes through io.zig, but it would ensure everything is working. To prevent regressions I was thinking a simple lint or script that looks for anything std.io
, std.fs
, std.posix
etc that isn't in src/io.zig
and fails CI
I don't think it is just std io. But that is the core of it.
Yeah, I think we're on the same page.
also threading and I believe atomics and mutex and such.
Also I've wondered if there's good debug tooling out there in WASM land that might help us understand / isolate memory issues and the like.
Anyway, yeah, cut it however you like. I still think. any new supported target should be added here 100%:
https://github.com/roc-lang/roc/blob/c3f320e58be57d21e12ccc82246ff738cb647f91/.github/workflows/ci_zig.yml#L139-L148
debug tooling in wasm land
In our company we work on wasm obfuscator in rust. Recently we came across a super subtle bug in memory obfuscation in prod builds. Turned out we missed an operation in memory encryption on wasm pages boundaries. It was a needle in haystack. A week of work of three developers. The only thing helped us is reproduction in debug build (we had to randomize obfuscation seed in a loop till bug is reproduced) so we kept only memory obfuscation. We had a pretty readable wat and just stepped trough the bug. I'm sure dwarf could also help but we found the root cause sooner.
Long story short we didn't find anything meaningful that could help us with memory analysis of wasm. Only browser devtools. But we're tied to browser environment with gluecode in js so we didn't have many options to explore.
Last updated: Jul 26 2025 at 12:14 UTC