14 ms·
Snappy UIs with WebAssembly and Web Workers
- amelius 3y agoCan we have structural sharing between web workers please? Because serializing everything as a message stream is not the most efficient way to go about many things. Also not the most programmer-friendly.
- eole666 3y agoInteresting article, but it's lacking a performance comparison between wasm and javascript running in workers. JS can be largely fast enough for a lot of use cases: if I had some WebAssembly to a project I need a good reason to do so.
- postalrat 3y agohttps://takahirox.github.io/WebAssembly-benchmark/ https://takahirox.github.io/WebAssembly-benchmark/ JS can be faster in enough cases where it's worthwhile to test. WASM is mostly being used for code that has already been written and is now being integrated into a web site. I wouldn't suggest jumping right to WASM simply for performance.
- afavour 3y agoI'm fascinated by WebAssembly and love that it exists but if anyone tells you they need to use WebAssembly to make the UI snappy I'd advise you interrogate that assertion thoroughly. I don't want to speak to this example too deeply because I don't know it (I see they're doing all sorts of stuff with audio so maybe they do need WebAssembly) but modern JavaScript VMs are very, very fast. 99% of webapps are absolutely fine using JavaScript. The far more important part of making snappy UIs is the Web Worker aspect. To my mind it's one of the key reasons native apps feel so much better than web ones: it's trivial to move an operation off the main thread, do an expensive calculation and then trivial to bring it back to the main thread again. Unfortuately the API for doing so on the web is extremely clunky, passing messages back and forth between a page context and a worker context. I'd recommend anyone thinking about this stuff to take a look at Comlink: https://www.npmjs.com/package/comlink https://www.npmjs.com/package/comlink it lets you wrap a lot of this complication up in simple promises, though you still have to think hard about what code lives inside a worker and what does not. In my ideal world all the code would live in the same place and we'd be freely exchanging stuff like you can in Swift. "Don't do it on the main thread" feels like a mantra taught to every new native developer as soon as it can be, the same discipline simply doesn't exist on the web. But neither do the required higher level language features/APIs.
- o1y32 3y agoVery disappointing to see that there are almost no benchmark quoted in this thread, only words like "snappy" or hypothetical questions. I have been reading articles to understand if converting some of our JS code to WebAssembly could lead to significant performance improvements and other benefits, and I have yet to come to a conclusion. I have seen dumbed-down examples that don't actually mean anything in the real projects, some anecdotal numbers, and other performance comparisons based on proprietary codebase which don't really help. I would advise that people be cautiously optimistic about WebAssembly -- the benefits may not be worth the effort.
- azemetre 3y agoHave you seen the video posted in the parent, by the creator of leptos (a rust webassembly framework)? https://www.youtube.com/watch?v=4KtotxNAwME https://www.youtube.com/watch?v=4KtotxNAwME The benchmarks being displayed have leptos beating react in nearly every category.
- deleted 3y ago[deleted]
- ramesh31 3y ago>I'm fascinated by WebAssembly and love that it exists but if anyone tells you they need to use WebAssembly to make the UI snappy I'd advise you interrogate that assertion thoroughly. The Figma team would say differently. For 99% of CRUD app use cases you are absolutely correct. But WASM has enabled functionality on the web we could have only dreamed of 5 years ago.
- jauntywundrkind 3y agoIt's an impressive app but I as the parent said, I'd expect JS would have worked just fine & let you do all the same things, at essentially the same speed. The key differentiation, as the parent says, is using Web Workers to make sure you're not doing work on the main thread.
- kevingadd 3y agoI would caution anyone looking to make their UI "snappy": while webassembly performance for things like raw compute - especially with simd - is superior, any time you need to interface with browser APIs like the DOM to update your UI you're now going to pay a bunch of interop costs that aren't present in native JavaScript. For some workloads this will make wasm meaningfully slower - especially ones that use strings.
- eyelidlessness 3y agoOr really just anything “chatty” with the main thread. Doesn’t have to be strings, or DOM, just heavy cross-VM/cross-thread interaction.
- samwillis 3y agoWASM and Web Workers - unless carefully used - won't magically make your UI snappy. There are three reasons (for the vast majority of apps) that a UI feels sluggish: 1. The network! Requesting data from a server is slow, by far the slowest aspect of any app. As a start, prefetch and cache, use a CDN, try edge platforms that move data and compute closer to the user. However, if you can explore Local First (http://localfirstweb.dev http://localfirstweb.dev), for a web app it is the way we should be looking to build in future. 2. They are doing work on the UI thread that takes longer than 16ms. This is where Web Workers are perfect, the DX around them isn't perfect, as another comment suggested Comlink helps, but there is a lot of opportunity here to build abstractions that help devs. 3. Excessive animations that delay user interaction - there is so much bad UX where animations have been added that only make things slower. Good animations have a purpose, showing where something came from or is going, and never get in the way. Finally, we are well into diminishing returns with front end frameworks optimising the way they do templating and update the DOM. The key thing there now is DX, that is how you pick a framework, benchmarks are almost always useless.
- KRAKRISMOTT 3y ago>Good animations have a purpose, showing where something came from or is going, and never get in the way. Good animations also help reduce perceived delays
- jalk 3y ago> Good animations have a purpose, showing where something came from or is going Many websites will slowly slide a newsletter sign up form up from the bottom. We already know that it comes from the underworld, so the animation absolutely pointless there.
- fenomas 3y agoWeb Workers are a really great feature for performance. The way they want the worker code to live in a separate file makes them slightly annoying if you're using a bundler, but each bundler has a loader or similar feature for this, so all is mostly well. But the thing I haven't found a solution for is, the case where you want to use web workers inside a library that other people will be importing into their own project, and you don't know what bundler they'll use (or transpiler, minifier, etc). I can think of hairy ways to do it, that involve pre-building the worker JS and storing that text in your library, piping it into a file blob at runtime, or the like. But does anyone know a clean way of handling this?
- sickill 3y agoI’ve been looking for an answer for this exact problem as well. There doesn’t seem to be anything out there that doesn’t involve some awkward hackery…
- strogonoff 3y agoDesign in layers. Layer 0: Strategically separate core logic while assuming as little about the environment as possible. Function Y generates something, function X handles the result somehow. Maybe there’s a postMessage somewhere between, or maybe not—you don’t care. Maybe Y is slow, but that doesn’t mean it must assume it runs in a worker. Maybe X serializes output in some way, but it doesn’t need to assume that DOM exists yet. However Y and X are wired up later is none of their concern. Layer 0.5: Document intended or just practical ways to invoke those APIs. Y is slow, call it from a worker. X formats stuff, so if you’re in a browser you’ll want to hook it up to DOM somehow. Layer 1: Provide glue functions to wire your core logic up in different environments. Worker message handlers? React components? These things could require more specific environments to be called in, and they would use Layer 0 APIs—but, crucially, your layer 0 won’t fail at its core task if there’s no DOM or postMessage. Maybe your user doesn’t want Y to run in a worker, or manages own web worker pool, etc. Layer 2: Provide last-mile facilities and helpers. This outer layer is technically outside of your actual library implementation. Bundler configuration templates for esbuild? Webpack? Example projects? Template repositories? Single-file bundle that spawns a worker for simplest use cases or demos? Anything’s great here—though note that if you support too many options there’s a good chance some of them will become stale, which can hurt adoption, and you don’t want to spend too much time on this layer as it’s probably the least important and the most flaky as specs, environments, build tools and trends evolve. (That’s also the reason why commingling this stuff, with all of its runtime/environment concerns, and your actual library is probably a very bad idea. If your library always spawns a worker at runtime, someone may certainly curse.) Such a design should maximise your library’s utility. Somebody doesn’t want Y to run in a worker for some crazy reason? They are always free to wire up core functions in whatever way they want. Another user has a complex project that manages own worker pool? They’ll probably eject after layer 1. Ensuring as much as possible is at lower layers, strategically separated, means you will have easier time iterating on higher layers to support different environment scenarios or bundlers, and you (or your users!) can add support for any new runtime configurations that appear in future without touching the core parts.
- nwoli 3y agoWith all this hype around full gpu UIs it rarely mentions the high latency around that. I’m much more excited about wasm making things snappier
- brundolf 3y agoEven just JavaScript Web Workers can be really helpful for doing heavy compute outside of the UI thread. JavaScript is pretty fast, it just needs to be unblocked. I used them once to sort a six-digit array of objects client side, while keeping the UI snappy (it took a couple seconds to process, but the UI was responsive the whole time) Of course for some tasks you'll still need more than that, which very well may have been true for the OP, but benchmarking is good etc
- austin-cheney 3y ago> Running Fast(er) With WebAssembly How much faster? I have to ask because people frequently make claims about perform based upon unmeasured assumptions that are wrong more often than not. Worse than that, many of these imagined claims about performance tend to be off by one or more orders of magnitude.
- whoomp12342 3y agoI love the idea of blazor, but I have seen first hand that it can be slows as balls. But that might not be WASMS fault, it could be .net
- mattlondon 3y ago> runs at speeds you would not be able to achieve with just JavaScript Is that true? Last I heard wasm was significantly slower than vanilla JavaScript in synthetic benchmarks. Has that changed recently?
- tamimio 3y agoI’m interested too to see those benchmarks
- logankeenan 3y agoI have experimented with something similar by embedding an entire Rust Axum server in a service worker. I wrote a blog about it recently. https://logankeenan.com/posts/client-side-server-with-rust-a-new-approach-to-ui-development/ https://logankeenan.com/posts/client-side-server-with-rust-a...