Stream: show and tell

Topic: Roc JS DOM platform experiment


view this post on Zulip Luke Boswell (Nov 25 2024 at 01:43):

@Antonin Komenda asked about client side Roc... so I made a small demo using wasm-pack.

https://github.com/lukewilliamboswell/roc-experiment-js-dom

view this post on Zulip Luke Boswell (Nov 27 2024 at 09:09):

I've made some major upgrades to this demo... we now have (partially) working Action-State. :smiley:

I almost got it fully working, but ran into a lambda set issue specifically with handling events. I've got a workaround using an update function and I hope this will be easy to remove in future.

Anyway, here is a demo of the click example (inspired by the counter example from Action-State proposal).

js-dom-3.gif

I'm pretty stoked with the performance and size. This demo .wasm is only 190KB for the debug build**.

The API is a bit rough... and the "events" are hardcoded to onclick, but it's pretty much all there and working.

Hope to see someone build something cool with this.

[edit] ** actually by default wasm-pack builds without debug symbols, see https://rustwasm.github.io/wasm-pack/book/commands/build.html we need to use --dev to get the full debug symbols and run debug assertions etc, which blows the size up to 1MB.

view this post on Zulip Luke Boswell (Nov 28 2024 at 00:58):

I wanted to explore what was taking up so much space in the minimal example. I suspect most of it is rust std library stuff which we can remove by adding #![no_std]. This would mean we'd have to roll our own no_std VDOM in rust, but that should be doable I think.

view this post on Zulip Luke Boswell (Nov 28 2024 at 00:58):

Here's the smaller size branch https://github.com/lukewilliamboswell/roc-experiment-js-dom/tree/minimal-size, I just hacked some things out and made sure it was still calling roc in a meaningful way.

view this post on Zulip Luke Boswell (Nov 28 2024 at 00:58):

So basically this no_std host impl...

#[wasm_bindgen]
pub fn run() {
    console::log("INFO: STARTING APP...");

    let boxed_model = roc::roc_init();
    let roc_html = roc::roc_render(boxed_model);

    assert_eq!(
        roc_html.discriminant(),
        roc::glue::DiscriminantHtml::Element
    );

    console::log("HELLO");
}

view this post on Zulip Luke Boswell (Nov 28 2024 at 00:58):

Gives us these sizes...which when compressed is around 11KB for a minimal hello-world.

Screenshot 2024-11-28 at 11.49.36.png

$ ls -hl
total 64
-rw-r--r--@ 1 luke  staff   397B 28 Nov 11:49 package.json
-rw-r--r--@ 1 luke  staff   1.5K 28 Nov 11:49 web.d.ts
-rw-r--r--@ 1 luke  staff   4.6K 28 Nov 11:49 web.js
-rw-r--r--@ 1 luke  staff   579B 28 Nov 11:49 web_bg.wasm.d.ts
-rw-r--r--@ 1 luke  staff    11K 28 Nov 11:49 web_bg.wasm.gz

view this post on Zulip Richard Feldman (Nov 28 2024 at 01:19):

Luke Boswell said:

we'd have to roll our own no_std VDOM in rust, but that should be doable I think.

I think @Brian Carroll may have already made some progress in this direction if I remember right?

view this post on Zulip Richard Feldman (Nov 28 2024 at 01:20):

11K compressed for the hello world Action-State application (without a real host) is smaller than I would have guessed! :smiley:

view this post on Zulip Richard Feldman (Nov 28 2024 at 01:20):

maybe with a real (no_std) host the whole thing could end up being smaller than React

view this post on Zulip Richard Feldman (Nov 28 2024 at 01:21):

probably not Elm and certainly not Svelte, but React seems very plausible with that number as a starting point

view this post on Zulip Brian Carroll (Nov 29 2024 at 18:22):

Richard Feldman said:

I think Brian Carroll may have already made some progress in this direction if I remember right?

No I didn't use any Rust at all. My unfinished project was almost entirely in Roc with some JS and a tiny amount of Zig. It's still in the repo and there's a README explaining what I did and didn't get done.
https://github.com/roc-lang/roc/tree/main/examples/virtual-dom-wip
Basic idea was to do the diffing in wasm and then call out to some JS functions to apply the patches. That minimises the amount of data you need to send over the Wasm/JS boundary (that is a bottleneck since everything needs to be serialized).


Last updated: Jul 06 2025 at 12:14 UTC