Stream: platform development

Topic: Help writing a Wasm4 platform


view this post on Zulip Hannes (Nov 21 2023 at 00:48):

I'm very interested in trying Roc for gamedev, but this obviously needs a platform for each game engine. I know @Luke Boswell is working on a graphical platform, but I suspect that's still pretty for from being ready to handle everything a game platform requires.

I recently remembered WASM4, it's a fantasy console similar to PICO-8, but instead of using Lua, it uses any language that can target WASM. I think this would be a great way to try Roc for small game projects because the engine can handle all the input, graphics and sound, and it has an intentionally small scope which will keep the Roc API simple.

I've started working on a platform in Zig based on the existing WASM platform, but I have pretty much no Zig experience, so I'm going to need a lot of help!

view this post on Zulip Hannes (Nov 21 2023 at 00:50):

First thing I need help with: running the WASM platform from the Roc repo!

I'm getting this error when I try to build the app:

❱ roc build --target=wasm32 examples/platform-switching/rocLovesWebAssembly.roc
🔨 Rebuilding platform...
An internal compiler expectation was broken.
This is definitely a compiler bug.
Please file an issue here: https://github.com/roc-lang/roc/issues/new/choose
thread '<unnamed>' panicked at 'cannot find `glue.zig`. Check the source code in find_zig_glue_path() to show all the paths I tried.', crates/compiler/build/src/link.rs:123:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'Failed to (re)build platform.: Any { .. }', crates/compiler/build/src/program.rs:1021:46

view this post on Zulip Luke Boswell (Nov 21 2023 at 01:17):

Yeah, that is a bug. If you run it with cargo run -- build --target=wasm32 ... I think that is a workaround. The issue is that the roc cli is hardcoded to look for a glue.zig file and it is located relative to the executable. This definitely isnt needed, its just an interim workaround that is ready to be removed from roc cli.

For building a wasm platform I highly reccomend using zig and a build.zig similar to my https://github.com/lukewilliamboswell/basic-graphics platform. That will be a much nicer experience.

view this post on Zulip Luke Boswell (Nov 21 2023 at 01:18):

I'm definitely keen to help out with this project, sounds like fun!!

view this post on Zulip Luke Boswell (Nov 21 2023 at 01:21):

Also I have a super simple WASM platform which may be helpful to look at as it https://github.com/lukewilliamboswell/roc-serverless

view this post on Zulip Hannes (Nov 21 2023 at 01:22):

Thanks Luke, I suspected this was something that had a workaround, those links will definitely be super helpful :thumbs_up:

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 01:22):

That's a really fun idea. Also, yeah, the wasm build target is currently very hobbled together. Like roc has great wasm code gen (including dev backend) , but the wasm linking and platform generation is basically hard coded to some zig stuff that just happened to be convenient at the time

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 01:23):

Depending on how wasm4 works, it may be worth starting by just compiling roc to a wasm library and then loading it from a wasm4 application. That may be easier than full platform setup at the current moment.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 01:25):

Aside, maybe this could be a good project for us to look at for cleaning up wasm linking and making it work more generically.

view this post on Zulip Hannes (Nov 21 2023 at 01:39):

Brendan Hansknecht said:

compiling roc to a wasm library and then loading it from a wasm4 application

Sorry for my ignorance about wasm, I haven't heard of wasm libraries. It sounds like it could be possible to compile all the Roc code into a wasm library, then import that into a Zig program that is compiled to a .wasm file, is that right?

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 01:41):

So my understanding is we would compile all roc code to a .wasm file then make zig generate a .wasm that links to and includes the roc .wasm file. So give zig control of the final build process to get around all of the hacky stuff we do when roc controls the build process.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 01:43):

Not sure how much time I'll have, but I'll look at making a really basic wrapper to show what I mean. That should at least be a base that could be built off of.

view this post on Zulip Luke Boswell (Nov 21 2023 at 01:48):

The platform build stuff is really easy I thknk using build.zig. The part of this project that I think will be a challenge to get started is the interface between roc and Zig. We dont have a zig-glue.roc spec yet, so you have to roll these by hand. @Brendan Hansknecht was potentially going to look at implementing a basic init, update, render for the roc-graphics-mach experiment. Maybe this would be a better place to do that. This looks like a more mature game engine to target. I'm not confident I can build the zig-roc interface, but once we have that it should be pretty easy to add more capability. I could make a bare bones platform pretty quickly, with the build process etc just copying what I already have elsewhere. But without the integration we'd have to bundle a full json implementation from zig in wasm and use json between roc and zig which is less than ideal. It might be OK to get us started though.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 02:11):

yeah, I think it would be cool to make this work. I do agree that zig glue would help a lot. I may be one of the few peoples that will happily write glue code by hand and who has the knowledge to generally do so correctly-ish.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 02:15):

Working on this or working on zig glue would both probably be good things for me to do/to stream.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 04:18):

I just noticed that wasm4 supports rust first class. I assumed that it would just be a c project, but it has wrappers for tons of languages. If we want this platform to build robust and working soon, I would advise just making it with rust. Given rust glue already exists, it would be much more seamless to make a really nice platform. That said, pushing zig stuff is also important. So that could be the focus instead. Just depends on goals.

view this post on Zulip Luke Boswell (Nov 21 2023 at 05:27):

I spent an hour or so this afternoon looking at it with zig, and what I thought would be really straightforward isnt. I think this is a good project to sort out the roc build process, wasm32 makes this interesting. But totally agree, if we can use rust it would be much quicker to flesh out a working platform for actual gamedev.

view this post on Zulip Luke Boswell (Nov 21 2023 at 09:02):

I made a start on this, got something basic that doesn't work. https://github.com/lukewilliamboswell/roc-wasm4-games

view this post on Zulip Luke Boswell (Nov 21 2023 at 09:04):

So the issue I'm facing is that Roc expects things like roc_malloc to be provided by the wasm runtime, however this isn't provided by the default w4 run myCartridge.wasm

view this post on Zulip Luke Boswell (Nov 21 2023 at 09:05):

So, I'm not sure it will be possible to make a platform for this without forking the wasm4 runtime and providing the functions that Roc requires to be provided.

view this post on Zulip Luke Boswell (Nov 21 2023 at 09:06):

This will be the same issue with Rust too

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 14:55):

Why is that a problem? I'm sure that zig has access to some chunk of memory that it can expose to roc. Wasm4 shouldn't need to expose it to roc.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 15:20):

Specifically, zig will have to expose a memory allocator that give some of the available program memory lifted here to roc: https://wasm4.org/docs/reference/memory/

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 15:28):

Probably the simplest solution. Would be for the actual model to be stored in separate memory but for each frame to just use a clean arena allocator. Though given zig dowsnt know the exact size of the model, that may not be easy.

In the worst case, a simple impl of malloc and free needs to be added on top of that available memory.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 18:12):

Screenshot-2023-11-21-at-10.11.10AM.png

wrote a very messy and primitive allocator that uses the program memory space labeled by wasm4.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 18:12):

So yeah, everything should be doable.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 18:18):

Also, I don't fully understand our wasm build pipeline and how to make zig build something that we can have roc build on top of. So in my current testing, I am just having zig control the full build (which may always be needed for this platform due to the memory restrictions and such, but not fully sure.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 18:22):

Fork here of luke's work as a reference: https://github.com/bhansconnect/roc-wasm4-games

view this post on Zulip Luke Boswell (Nov 21 2023 at 18:37):

So good :+1:

view this post on Zulip Luke Boswell (Nov 21 2023 at 22:09):

@Hannes take a look and let us know what you think. With Brendan's modifications this works and we have a functional platform for Wasm4.

All you can do with it right now is pass a string back and forth, but I have used just that functionality within the platform to still expose a nice API for apps in roc-graphics-mach so there looks to be nothing stopping us from building a much more featureful platform for developing wasm4 games.

We can also potentially look at making a nicer Zig-Roc interface than just passing a Str.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 22:12):

Also, there is probably a smarter way to do malloc and free. I just use a bit set and a list of used locations. Originally wanted to just use the bitset but forgot that roc_dealloc doesn't get passed a size currently. If that gets fixed, this can be made a lot slimmer.

view this post on Zulip Luke Boswell (Nov 21 2023 at 22:13):

4.3K isn't small enough? :sweat_smile:

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 22:16):

That is with a limit of 200 allocations. At the max possible number of allocations, it is 33k.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 22:16):

If roc gives us the size info, it can be 4k with the max possible number of allocations being allowed.

view this post on Zulip Luke Boswell (Nov 21 2023 at 22:25):

Is there plans for roc_dealloc to get passed a size? Sounds useful

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 22:32):

Its been discussed before and on my long list of eventually want to implement. Biggest change will be to seamless slices, but overall still feels quite worth it. Does cost an extra usize on the heap per list/large string.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 22:33):

That said, the usize just exists in the allocator is not in the list/large string. So depending on the allocator, it has little cost.

Oh yeah, I can do that manually in this zig platform, let me fix that.

view this post on Zulip Brendan Hansknecht (Nov 21 2023 at 22:55):

Ok, now it is 3.8k and it avoids the extra overhead of a side table for tracking allocations. instead it just stores the size before the allocation. So can have lots of allocations without statically wasting tons of binary space. Does waste more memory though cause every allocation has 1 chunk of overhead. But I think it is a better tradeoff.

view this post on Zulip Hannes (Nov 24 2023 at 06:05):

Thanks Luke and Brendan, sorry for not responding earlier, I've been bust this week with starting a new job. That's great news that you've been able to get a proof-of-concept working! I'll find some time to get my head around how it works and how to build a proper API around it, probably just by passing a string to the platform for now like Luke suggested.


Last updated: Jul 05 2025 at 12:14 UTC