Stream: show and tell

Topic: Wasm4 Game Platform


view this post on Zulip Brendan Hansknecht (Jan 09 2024 at 05:16):

Luke Boswell and I are excited to announce a new platform for making games with Roc!
The platform creates games targeting the WASM-4 fantasy console (Thanks @Hannes for the idea).

roc-wasm4 wraps all the pieces necessary to fully utilize wasm4 from roc.

Current examples include:

Please try it out and let us know what you think!
All information needed to get started can be found in the README

view this post on Zulip Isaac Van Doren (Jan 09 2024 at 05:26):

Awesome!!

view this post on Zulip Hannes (Jan 09 2024 at 05:38):

Thanks Brendan and Luke for doing this! I was making such slow progress on my version of this platform because I was learning Zig and manual memory management at the same time as trying to build the platform :sweat_smile:

Very excited to try making some games in Roc! :tada:

view this post on Zulip Hannes Nevalainen (Jan 09 2024 at 05:51):

Wow :star_struck:

view this post on Zulip Johan Lövgren (Jan 09 2024 at 06:17):

Amazing

view this post on Zulip Agus Zubiaga (Jan 09 2024 at 16:14):

This is so cool!

view this post on Zulip Richard Feldman (Jan 09 2024 at 17:44):

is it cool if I tweet about this from the roc_lang account? I'd like more people to know about it! :smiley:

view this post on Zulip Brendan Hansknecht (Jan 09 2024 at 17:56):

We were planning to publish it more broadly after some people here play with it and we confirm there aren't any major issues, but probably fine to just share it.

view this post on Zulip Richard Feldman (Jan 09 2024 at 18:20):

either way - I'm also ok waiting!

view this post on Zulip Brendan Hansknecht (Jan 09 2024 at 18:27):

@Luke Boswell I'm good either way, what's your pick?

view this post on Zulip Luke Boswell (Jan 09 2024 at 18:47):

I think sharing is good, I'm hoping we dont have any major issues preventing people from using it... :sweat_smile: but I guess we can just fix those.

view this post on Zulip Brendan Hansknecht (Jan 09 2024 at 19:36):

Cool note for anyone interested:

You can get hot reloading for any changes to the update function by setting up two terminals:

Note, if you change the Model type, it will probably just crash due to trying to read the memory in an invalid way. But otherwise, it should mostly work. If it crashes, just press R for a fresh restart.

view this post on Zulip Luke Boswell (Jan 10 2024 at 02:59):

I've also posted this to reddit on the roc_lang and functionalprogramming communities :smiley:

view this post on Zulip Brendan Hansknecht (Jan 10 2024 at 06:56):

Extra note for anyone who tries roc-wasm4. Make sure to update your version of Roc. I had to add a few patches to Roc for this platform.

view this post on Zulip Steven Chen (Jan 10 2024 at 07:05):

So awesome! Great example to learn wasm platform. Roc as the ultimate functional embedded DSL :joy:

view this post on Zulip njlr (Jan 12 2024 at 22:30):

This is really cool!

However, I wonder if this approach is leaving a lot on the table performance-wise?

I looked at the code and it seems that the platform calls the update function each frame, passing in the previous model and getting the next. I guess this is like The Elm Architecture running a cmd every frame.

One of the things that excites me about Roc is that (in theory) it can optimize this pattern away , taking the frame update code from "return a new state" to "update the state in-place".

This would give us the classic imperative game loop, but written functionally:

while(true) {
  update();
  draw();
}

But this optimization cannot exist across the platform boundary?

Very new to Roc so curious what people think.

view this post on Zulip Luke Boswell (Jan 12 2024 at 22:35):

@Brendan Hansknecht and I discussed briefly a few different ways to do this, he will be much more capable of answering this question than I. But I think we decided to implement the simple and easy thing first to have something working, and now that we have that we can explore further ways to improve performance and developer experience.

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 00:05):

taking the frame update code from "return a new state" to "update the state in-place".

Roc can't have any state. So this is not possible in Roc. Of course you could use an effect to save state to the host, but that is just this model with extra steps.

In the future, if roc adds something like #ideas > Stored ability, it would then become possible to keep all of the state on the roc side. Though it would be saved and loaded through tasks. That said, roc understands the types fully, so probably could optimize that use case a bit more (and should avoid all boxing hopefully).

passing in the previous model and getting the next

This is just a single pointer being passed back and forth. So theoretically no real cost.

However, I wonder if this approach is leaving a lot on the table performance-wise?

For wasm4 were models are small, and allocations are a mostly linear array, probably not that much performance lost. For more complex use cases, probably a lot of performance lost.

That said, all of the real loss is in our current handling of Box. As it stands today, each iteration will take the boxed model, copy it to the stack, free the box, build a new model on the stack, allocate a new box, copy the new model into the box.

This has 2 core inefficiencies:

  1. extra allocations/deallocations and refcount changes.
  2. wasting time copying data to/from the stack

What we really want is:

  1. reading directly from the box without needing to copy the entire struct out
  2. never changing the refcount of the box cause it will be reused
  3. writing back to the box at the end without allocating a new one.

This would be the same flow as if we had just passed around a reference to a large struct and then updated it at the end. If we had a smarter compiler and a proper Box.update function, most of this loss should be mitigated.

All that said, I think it should be possible to remove the boxing and allocate a single storage space for the model (actually may take 2 to be safe). Storage would still live on the host, but it could be in an alloca on the stack or allocated on the heap just once. I'll look into doing that to safe from a bit of the extra cost of box. nvm, that isn't doable cause we don't know how the struct will be passed in. That depends on the exact struct implementation details. So have to box currently.

view this post on Zulip Richard Feldman (Jan 13 2024 at 01:42):

Brendan Hansknecht said:

taking the frame update code from "return a new state" to "update the state in-place".

Roc can't have any state. So this is not possible in Roc. Of course you could use an effect to save state to the host, but that is just this model with extra steps.

hm, but we could pass the state from the host to/from Roc (in update) and then, if the refcount is 1, Roc could update it in place, right?

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 01:50):

yeah, that is already what is being done.

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 01:50):

Except that Box never updates in place currently

view this post on Zulip njlr (Jan 13 2024 at 08:41):

Could the Roc platform have two effects available:

Then you would build a game-loop Task out of those and the Model would never cross the platform boundary?

gameLoop =
    \state ->
        deltaTime <- getTime
        nextState = update deltaTime state

        _ <- sendRenderInstructions (render nextState)

        gameLoop nextState

main =
    gameLoop initialState

I don't know what do-notation looks like in Roc (if any?) so please forgive the syntax!

view this post on Zulip Hannes (Jan 13 2024 at 11:43):

You would also need an effect to get the player input, right?

view this post on Zulip njlr (Jan 13 2024 at 12:34):

Hannes said:

You would also need an effect to get the player input, right?

Yes, and also for things like sound, but I was trying to simplify. getTime could be replaced by getTimeAndInputs for example, but the principle is the same.

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 16:50):

So make roc an infinite closure.

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 16:51):

I think that could theoretically work with other platforms, but probably not wasm4

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 16:52):

Wasm4 games don't even control their own execution in this manner

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 16:52):

They are split into an init and update function called by the wasm4 runtime

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 16:54):

And it is all single threaded, so no way for something else to run while roc is running

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 16:57):

Though maybe there is some way to split up tasks to always capture the model. Probably would require switching to an effect interpreter model.

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 16:58):

Oh, also, wasm4 has no sense of time except a manually added frame count

view this post on Zulip Isaac Van Doren (Jan 13 2024 at 23:54):

I’m having so much fun playing with this platform! Bravo :clap: :big_smile:

view this post on Zulip Luke Boswell (Jan 13 2024 at 23:55):

Thanks for letting us know, please let me know if you run into any issues :smiley: I would love it to be a really smooth experience, particularly for newcomers to the language

view this post on Zulip Isaac Van Doren (Jan 14 2024 at 02:17):

I just opened an issue; unused defs and imports are causing builds to fail for me

view this post on Zulip Luke Boswell (Jan 14 2024 at 02:21):

@Brendan Hansknecht and I talked about that briefly, without running roc check first zig build will gobble any warnings and errors and fail without explanation. Unfortunately roc check will return non-zero exit code for a warning. I think we might need to raise an idea thread to discuss changing that.

view this post on Zulip Brendan Hansknecht (Jan 14 2024 at 02:21):

It is worse than that.

view this post on Zulip Brendan Hansknecht (Jan 14 2024 at 02:21):

roc build will emit a non-zero exit code on any warning

view this post on Zulip Brendan Hansknecht (Jan 14 2024 at 02:22):

So zig will see that and assume it can't continue building

view this post on Zulip Brendan Hansknecht (Jan 14 2024 at 02:22):

Also, I don't think zig build gobbles the roc build errors anymore. So we probably could remove roc check now if we wanted, but given it catches extra errors, I left it in.

view this post on Zulip Brendan Hansknecht (Jan 14 2024 at 02:23):

we need something like roc build --ignoreWarnings for this to work

view this post on Zulip Isaac Van Doren (Jan 14 2024 at 02:25):

Ah gotcha

view this post on Zulip Isaac Van Doren (Jan 14 2024 at 03:39):

I started a related thread here https://roc.zulipchat.com/#narrow/stream/304641-ideas/topic/Changing.20exit.20codes.20for.20roc.20build.20and.20check/near/412777236

view this post on Zulip Brendan Hansknecht (Jan 15 2024 at 21:37):

This isn't fully done, but since it is functional and I am hitting a compiler bug for the last part, thought I would post it:

Rocci Bird!!!

view this post on Zulip Kiryl Dziamura (Jan 15 2024 at 21:55):

@Brendan Hansknecht dope animations!

view this post on Zulip Brendan Hansknecht (Jan 15 2024 at 22:02):

I can't claim any of them. I have a friend who loves to do pixel art and he made everything.

view this post on Zulip Richard Feldman (Jan 15 2024 at 23:17):

this is so rad!!! :star_struck::star_struck::star_struck::star_struck::star_struck:

view this post on Zulip Isaac Van Doren (Jan 15 2024 at 23:24):

Awesome! :roc: :bird:

view this post on Zulip Hannes (Jan 15 2024 at 23:45):

Haha, there goes my first idea for a Roc game :sweat_smile: I'll have to be more original when i actually start making games. Any chance you could release the Roc sprite for others to use, @Brendan Hansknecht?

view this post on Zulip Richard Feldman (Jan 15 2024 at 23:51):

ok my high score is 2^3, I'm satisfied with that :laughing:

Screenshot-2024-01-15-at-6.50.46-PM.png

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 00:36):

Any chance you could release the Roc sprite for others to use

For sure. They technically are already on github, but only in the code form. https://github.com/lukewilliamboswell/roc-wasm4/blob/8459de33dcf479b056432a24dd2dee0337c7dd5d/examples/rocci-bird.roc#L605-L639

view this post on Zulip Hannes (Jan 16 2024 at 03:19):

Thanks, I'll extract them for whatever game I end up making :) Does your friend have a name and/or website that I could use to credit them?

view this post on Zulip Luke Boswell (Jan 16 2024 at 03:21):

If you play rocci bird, when your bird crashes it shows the arists name, Art by Luke DeVault

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 22:00):

Full version of the game with plants, better end screen, credits, more randomness, and memory corruption fixes is uploaded now. game - source

Feel free to use any of the source code including the sprite code.

Hopefully the game is stable now and won't run out of memory or hit other perf issues. Went through a bit of a process fixing a major platform bug and cleaning up some of the code.

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 22:12):

Oh also, if you are using the roc-wasm4 platform, please pull the new code. Otherwise your game dev experience may get exceptionally frustrating as you deal with memory corruption.

view this post on Zulip Luke Boswell (Jan 16 2024 at 22:47):

This is very cool. It looks super great, Luke did a great job with that art! :heart:

view this post on Zulip Brendan Hansknecht (Jan 19 2024 at 07:02):

Two notes anyone interested:

  1. The build script for Roc-Wasm4 is now nicer and will successfully build with warnings. (So pull the repo)
  2. rocci bird tracks your high score.

view this post on Zulip Kiryl Dziamura (Jan 19 2024 at 07:04):

Thanks for the warning part, it was a bit distracting!

view this post on Zulip Isaac Van Doren (Jan 22 2024 at 03:19):

I made a drum machine using roc-wasm4 and it was a lot of fun! You can try it here or see the source.

It can make grooves like this :drum:
roc-drum-machine.mov

view this post on Zulip Isaac Van Doren (Jan 22 2024 at 03:29):

I have not been able to get the sound to play on a phone but it should work in a normal browser

view this post on Zulip Luke Boswell (Jan 22 2024 at 03:30):

Works on my Samsung S22 :grinning_face_with_smiling_eyes: - the sound that is

view this post on Zulip Isaac Van Doren (Jan 22 2024 at 03:40):

Great!

view this post on Zulip Brendan Hansknecht (Jan 22 2024 at 03:48):

Do you try to balance channels at all to avoid overwriting sounds?

view this post on Zulip Isaac Van Doren (Jan 22 2024 at 03:53):

No I don't balance them; I've found that in practice it works well as is. If you try to play all five sounds at once they don't all come through but that doesn't really matter for typical grooves

view this post on Zulip Isaac Van Doren (Jan 22 2024 at 03:54):

You can also kind of use the overwriting to your advantage sometimes to get new sounds

view this post on Zulip Brendan Hansknecht (Jan 22 2024 at 03:55):

Fair

view this post on Zulip Brendan Hansknecht (Jan 22 2024 at 03:56):

some_music.mp4

From my wife

view this post on Zulip Isaac Van Doren (Jan 22 2024 at 03:57):

Love it!

view this post on Zulip Richard Feldman (Jan 22 2024 at 03:58):

this is awesome!!! is it cool if I share this from the roc_lang twitter account?

view this post on Zulip Luke Boswell (Jan 22 2024 at 03:58):

Nice, I'm not sure how the guys in my office feel about this game :sweat_smile::musical_notes:

view this post on Zulip Isaac Van Doren (Jan 22 2024 at 04:02):

this is awesome!!! is it cool if I share this from the roc_lang twitter account?

Yes sounds great!

view this post on Zulip Isaac Van Doren (Jan 22 2024 at 04:04):

And thanks :big_smile:

view this post on Zulip Kiryl Dziamura (Jan 27 2024 at 21:33):

Ok, I made a maze with infinite levels (2 ^ 32 if to be more accurate) with simple generative melodies per level (beware of horrible sounds!)

It's not the final version (I have a couple of ideas where to move), but you can check the current state here:

https://kirdzi.itch.io/just-a-maze?secret=H82wQhk3TOiFbu94vs46WaVS6ww

I think next week I'll clean up the source code and publish it on github

view this post on Zulip Luke Boswell (Jan 27 2024 at 22:18):

This looks nice, love the simplicity :grinning:

view this post on Zulip Brendan Hansknecht (Jan 27 2024 at 22:41):

Are the levels seeded or are they the same on every single run?

view this post on Zulip Kiryl Dziamura (Jan 27 2024 at 23:09):

All the same. It doesn’t touch w4 random at all but uses the level id as the seed for xorshift

view this post on Zulip Isaac Van Doren (Jan 28 2024 at 04:40):

That's awesome!

view this post on Zulip Richard Feldman (Jan 30 2024 at 15:17):

@Kiryl Dziamura once you have the source code published, I'd be happy to share it via the roc_lang twitter account if you're cool with that! :smiley:

view this post on Zulip Kiryl Dziamura (Jan 30 2024 at 15:19):

sure! let me just figure out how to refactor all my mess with the help of the state monad :grinning_face_with_smiling_eyes:

view this post on Zulip Luke Boswell (Apr 21 2024 at 02:57):

A new release for roc-wasm4 with a few fixes and cleanup so that the examples work with latest main, and also using the new ! chaining syntax. :roc:


Last updated: Jul 06 2025 at 12:14 UTC