Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Zaplib – Speed up your webapp with Rust+Wasm (zaplib.com)
175 points by stevekrouse on April 8, 2022 | hide | past | favorite | 41 comments



Hey HN, one of the creators here! I made it at Cruise because it was super painful to develop https://webviz.io with manual memory management (lots of ArrayBuffers), WebWorkers, etc. I thought that there must be a better way.

Very curious to hear stories from other folks building intensive stuff in the browser. How are you dealing with performance issues in Javascript? Have you tried using Rust or C++ with WebAssembly for parts of your apps? How did you make 2d/3d rendering faster? Would you want to use something like Zaplib?

(You may have seen my blog yesterday about the subtleties about Wasm vs JS/TS: https://zaplib.com/docs/blog_ts++.html)


Curious as to what the JS bridge overhead is like for both latency and throughput. What would the performance look like if you tried to serialize DOM updates over the bridge and replay them on the JavaScript side so that you could build DOM UIs? Have you measured # of updates per ~16ms?

(I know there is some proposal that might allow WASM to interact with the DOM directly too, but I don't know how likely it will be merged.)


Overhead of calling into JS from Wasm is very low; DOM calls are no problem since the DOM call itself is much slower than the overhead of switching from Wasm to JS. See e.g. this article from ~4 years ago: https://hacks.mozilla.org/2018/10/calls-between-javascript-a...

There are even some libraries that try to mimic React directly from Rust, such as https://yew.rs/ (though in my experience they can be a bit harder to use than Zaplib, since they require you to learn about traits, closures, etc, and tend to also run into borrow checker issues more often -- all of those are things we try to avoid having to learn in Zaplib!)


> How did you make 2d/3d rendering faster?

Using the obvious way, GL shaders.

The amount of time spent on the CPU side is mostly irrelevant, preparing buffers that can only be done from JS code anyway.


Hot damn I nearly got a seissure from watching the demo page. I'm sure flickering mess I see with Firefox 98.0.2 wasn't what they intended!

I can't really say much else about it, but it looks interesting.

ADD: not complaining about the down votes, but I wasn't kidding. I think it's malfunctioning in my browser in a literally dangerous way. I would record a video of it if it wasn't so annoying to upload. I'm guess nobody else sees this?


Oof — sorry for the late response but I'm seeing this too now. Pushed a fix, should be up soon. Tnx for reporting!


I saw (and still see) the same thing.


Something clearly went wrong with my fix when testing it — I think we got it now! Thanks again both!!


Great work. Put it on my favourite list now. Do you have any tips for debugging? I use the same combo Rust-WASM for my Glicol music programming language:

https://glicol.org

The audio runs in AudioWorklet thread while I guess for Zaplib it runs in Workers, right? Did you use SAB in Zaplib?

For me, one use case is to use Zaplib for visualising the audio. The built-in canvas and 2d drawing is really slow. Yet one concern is for the support on Safari. It really has a slow support for SAB. I have to switch to postMessage on Safari as a compromise.


That looks awesome!

For debugging we support full Rust source maps in the browser (using DWARF). :D (This is something that wasm-bindgen doesn't support, for example!) See https://zaplib.com/docs/developer_environment.html#chrome-de... for how to set it up.

Since we also support running the Rust code natively (even when doing rendering!) you can also use that and attach whatever native debugger you like. That same page also has info on how to set up native debugging with VSCode.


really cool. I will try to test it on the weekend.


Re audio: yeah we currently only use Web Workers and we don't have audio support yet. We do use SAB, but we're thinking of adding support for running without SAB so you can use it with older browsers.


That would be a smart move as SAB also requires CORS now, which adds more trouble for getting started. It can be a feature to keep for some context that really needs to get rid of GC such as audio.


Yeah exactly; the headers are such a pain to set up!


I always encourage users to use vite, and it is quite easy for the dev server.

https://github.com/vitejs/vite/issues/3909

For deployment it's a different story then but for example netlify has a quite intuitive way to set headers for CORS:

https://github.com/padenot/ringbuf.js/blob/master/public/_he...


I've been using a similar stack (C++ and WASM) to build some simple applications and I enjoy it very much. For the UI components, text rendering and layout I use Dear ImGui [0] as I am very familiar with it and it allows me to implement GUIs very fast. The biggest convenience is that you can run the same code both as a native application and as a web app. The biggest drawback is you usually get 100% CPU usage when there is an active animation in the WebGL canvas because you need to redraw everything (similar to the OP's example).

If you are interested, checkout my Github template repo [1] - it contains a few examples:

[0] https://github.com/ocornut/imgui

[1] https://github.com/ggerganov/ggweb


That's exactly the stack I use as well: C++, OpenGL and Dear Imgui compiled via Emscripten. You can take a look at Hexmap [0] which is built this way.

All in all the performance is great for highly interactive stuff that does some fair amount of client side data processing.

[0] https://unit520.net/hexmap


Very cool! We do throttle our animations to the screen's refresh rate using requestAnimationFrame, so you shouldn't necessarily get 100% CPU unless the animation takes up the entire "frame budget" (on my machine the main example in the introduction is more like ~40-50% CPU on Chrome).


Usually not an issue with very interactive apps. We have a similar workflow targeting web and desktop, but Rust + WASM. Using BevyEngine for real-time apps, and Yew for web apps.


Oh cool; you're also using this for robotics I presume (based on your Twitter profile)? Makes sense; lots of data to process there.


This looks awesome. I have been playing with WASM and rust, trying to get rusqlite to work with persistence, among other things.

I notice that you also came to the conclusion that having all io async is not the way to go for rust.

How do you wrap the async js io apis so they can be used in sync rust? I have seen different approaches for this: 1. an additional service worker / thread and atomics. 2. rewrite the entire WASM in some sort of continuation passing style.


We used approach #1: https://github.com/Zaplib/zaplib/blob/7976d39775dd1642f20d8b...

I might write a blog post about this, since it's pretty fun / tricky stuff!


Yes, I also went with the 1st one.

Basically the main thread sends a command to an io executor thread, and then waits on the result, which is done using atomics under the hood.

See e.g. https://github.com/cloudpeers/radixdb/blob/sqlite/browser/sr...

Can't wait to play with zaplib. Can you also use it as just a standard library without the GUI part?


Nice project, although pretty opinionated. But I guess you have to do that when targeting the crazy browser environment with all its weird quirks (at least for non-web devs)..

Somewhat related is a library I've been working on to generate an ffi between Rust and js code: https://github.com/cloudpeers/ffi-gen

Also I really like your universal_thread abstraction, hiding the whole web worker mess. I did a similar thing here: https://github.com/wngr/wasm-futures-executor


This is really cool but I can’t say I’m a fan of Zapium given it would move to a commercial license in the future. If you have to ship CEF anyhow, what is the performance advantage to licensing and using Zapium over just compiling WASM and shipping binaries with Electron? FWIW — this is how Java, .NET, etc packaging is done for interop with Electron.

I can understand from an ease of use perspective to have the bridge in between but it wouldn’t be worth subjecting a codebase to commercial licensing IMO. It’s not a whole lot more work to use process calls instead there so it seems an odd choice to commercialize that aspect in particular.


What's Zapium? I searched for it, but nothing relevant came up.


Looks to be this: https://zaplib.com/docs/zapium.html

> Zapium is the native, cross-platform Zaplib runtime. It converts Zaplib web apps to desktop apps, where the Rust code runs natively, not via WebAssembly.

> This is Zaplib's equivalent of Electron -- for an extra speed boost.


Thanks


>> this is how Java, .NET, etc packaging is done for interop with Electron

not certain if this should be the status quo for the longer term.


Super cool! I'm sure this is highly situational, but roughly how much faster can Rust -> WASM be than vanilla JS? 2x, 10x, 100x?


Great question! I wrote a blog post yesterday going into the nuances of this a bit: https://zaplib.com/docs/blog_ts++.html The short of it is that Rust+Wasm isn't much faster than highly optimized JS in most situations, but it's hard to optimize it. In Rust it's a lot easier to get to the same level of performance as highly optimized JS.

We also support building compiling to native though, and we've found that the typical difference between Wasm and native apps is about 2x (on most metrics).


Wow, only a 2x difference? That is fantastic on the Wasm side.


Yeah, Wasm is quite fast these days, and there is still more room to grow!


Great to see more work in the Rust+Wasm space. Interested in trying this out at some point.


Just tried opening this on my cheap Android phone; really appreciate that the whole page stayed usable! There was some noticable lag though, which I don't see on advanced Three.js apps. So some further performance tuning might be needed :)


Yeah, might be the continuous animation that we're doing! Or do you mean the startup time? There's definitely still a lot of room for us to optimize stuff :)


Yeah, it's probably the animation. The startup time was super fast for me! :)


None of the demos work in Safari on iOS.

iPhone XS on iOS 15.0.2


Safari only very recently added support for threading in Wasm, which we use. Try upgrading to the latest iOS (15.4.1)?

It's definitely bleeding-edge stuff; we should add some backwards compatibility for (slightly) older browsers though.


Works on 15.4 for me.


this is dope!!




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: