4 ms·
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
by afavour 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.
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.
- karaterobot 3y ago> I'd expect JS would have worked just fine & let you do all the same things, at essentially the same speed I think you would be wrong about that. Certainly, it's not just WASM that makes Figma fast, but it's an important piece. Look at it this way: why would they have built out the product with WASM five years ago if Javascript was just as good for their use case? It's a huge investment in a new technology with relatively few people you can hire for it, versus quite possibly the most well-known, well-supported language in the world. They identified some significant advantage that made the cost-benefit analysis line up. Normally, I don't like the argument that because somebody did it, it must be the right choice. But, Figma made so many correct decisions out of the gate that, in this case, I'd give them the benefit of the doubt.
- crabmusket 3y agoFigma were already compiling C++ to asm.js, and then switched over to WASM. https://www.figma.com/blog/webassembly-cut-figmas-load-time-by-3x/ https://www.figma.com/blog/webassembly-cut-figmas-load-time-...
- treis 3y ago> They identified some significant advantage that made the cost-benefit analysis line up. I don't know if this is true or not but I don't rate the fact that they used it as much evidence that they should have used it. IME decisions like this are because some early engineer wanted to use that particular bit of technology.
- deleted 3y ago[deleted]
- eyelidlessness 3y agoI think they’re vastly understating/oversimplifying how they use WASM. For audio analysis and operations on audio binary/encoded data, it’s quite possible that WASM is a very good fit. Maybe offloading that work to a JS worker would be sufficient—you’re right that JS VMs are extremely fast—but I wouldn’t necessarily discount this WASM use case either. And I’m saying that having explored these kinds of optimizations enough to generally recommend against using WASM unless you have strong reason to believe you’d benefit from it (ideally with benchmarks to prove it). I’d also caution that using Web Workers isn’t always so obvious either (and the same applies to server side threading eg on Node). There’s significant overhead (runtime) in spinning up a worker, and in every communication between threads—enough to negate their benefit for many use cases. Both WASM and workers have roughly the same perf downsides, and both should generally be justified by real measurement of their impact.
- kettlecorn 3y ago> 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. This is one of the advantages to using WebAssembly: you can use a language like Rust that makes multithreaded code easier to write and faster to run. Out of the box with JavaScript if you want to share an object with a worker you need to send a message (which internally serializes / deserializes the object) or manually manage serializing / deserializing bytes to a SharedArrayBuffer. With Rust/Wasm + Web Workers you can use Rust's synchronization primitives and just pass a pointer to the worker. You pay no cost for serialization / deserialization. > if anyone tells you they need to use WebAssembly to make the UI snappy I'd advise you interrogate that assertion thoroughly. As you pointed out the JavaScript VM is incredibly optimized but where Wasm shines is consistency. JavaScript requires some care to not produce garbage every frame, otherwise you may unpredictably trigger a lengthy garbage collection. With most Wasm languages you can easily avoid allocating any JavaScript garbage. The lower-level Wasm languages also tend to be easier to reason about performance. When assessing Rust code performance you typically want to look for Big-O and excessive memory allocations. If you do find a performance bottleneck typically you know where to try to improve. With JavaScript you'll sometimes hit situations where you fall off the VM's golden path, or where one JavaScript VM performs fine and another does not.
- deleted 3y ago[deleted]
- ch_sm 3y agoThose are good recommendations, and they confirm my own findings and experience trying to improve performance for Spectrogram and Waveform generation in a heavy audio focused web app. AssemblyScript and Rust/WASM implementation of these relatively simple but computationally heavy algorithms didn’t result in any meaningful improvements of their JS counterparts. In the end moving computations that took 10ms or more (some up to 700ms) off main thread and using tooling to simplify the WebWorker API was definitely more gainful, simpler and better for code hygiene, and in most cases as fast or faster than the WASM implementations.
- jcelerier 3y ago> 99% of webapps are absolutely fine using JavaScript. as a user, much less than 99% of apps are "absolutely fine" to use.
- renegade-otter 3y agoThis is not a Javascript problem, this is a Javascript ecosystem problem. A standard web page will load megabytes of JS code that was packaged from hundreds of dependancies for the reason that "this is how everyone does it". I just finished a UI that requires a few HTTP requests to the API and a bit of dynamic behavior (but not a ton), and it's done inline with ES6, with no transpilers or minifiers, using ArrowJS. It does the job, and it rips.
- chii 3y ago> This is not a Javascript problem, this is a Javascript ecosystem problem. you cant really divorce the language from its ecosystem.
- renegade-otter 3y agoI am telling you - I did :) but it's not possible in a professional environment.
- o1y32 3y agoAlso developers' problem. I just saw code that imports a 1M library to use one function that would take anyone no more than 10 minutes to implement. Maybe it is not too bad in the end because of tree shaking etc, but this kind of thing happens all the time.
- jayd16 3y agoHaving them in wasm won't always improve them so this is a bit of a statistical fallacy.
- afavour 3y agoYou are arguing a very separate point, one that I tackle in the next paragraph.
- rapnie 3y ago> if anyone tells you they need to use WebAssembly to make the UI snappy I'd advise you interrogate that assertion thoroughly. Get prepared to be blown away by Makepad [0]. I have no affiliation with them, but just watched their most recent conference presentation [1]. The slides were made with Makepad itself and included, embedded, a full-blown IDE, a synthesizer app, a Mandelbrot to zoom in endlessly, and more. All running at 120fps. The presentation is for the most part live-coding with this setup. What they want to do is bring coders and designers closer together, and while some code is in Rust they developed a DSL for the GUI parts that is close to how Figma works. These GUI's can run anywhere. And I couldn't help thinking "Why would people have complicated stacks to create Web 2.0 apps for the Google Web, when they have this?", in other words an opportunity to break out of the browser straitjacket. Btw. WebAssembly/WebGL isn't the only way in which Makepad is available. And while running well in the browser for a time, there were issues to be solved here (addressed in the presentation). And tbh this isn't a real answer to your assertion. Greg Johnston, creator of Leptos, has made a video with performance comparisons [2]. Edit: Adding a link to the synthesizer app I just found [3]. [0] https://github.com/makepad/makepad https://github.com/makepad/makepad [1] https://www.youtube.com/watch?v=rC4FCS-oMpg https://www.youtube.com/watch?v=rC4FCS-oMpg [2] https://www.youtube.com/watch?v=4KtotxNAwME https://www.youtube.com/watch?v=4KtotxNAwME [3] https://makepad.nl/makepad/examples/ironfish/src/index.html https://makepad.nl/makepad/examples/ironfish/src/index.html
- evilduck 3y agoRendering your UI entirely in a canvas tag isn't a new idea. If you have ADA compliance requirements or just want to build E2E tests, good luck.
- wim 3y agoWe’re building an "IDE for notes/tasks" [1], so as an editor of sorts, UI snappiness matters a lot for us too. The approach we’re taking is to basically split up the app in two parts (we refer to these parts as "frontend" and "backend", but they are both on the client). The frontend does all the rendering for the editor, which we want to stay within the frame budget. That's why we offload all data synchronization work (applying CRDT deltas, encrypting/decrypting data to/from websockets, IndexedDB caching, search, parsing JSON and so on) to the "backend" thread. I think for apps like this, splitting the UI and data part up (kind of like a frontend and a backend in a classic client/server web app) is very useful to prevent blocking the main thread. In our case this "backend" is actually a SharedWorker, which has the added benefit that it's very easy to keep all state in sync with multiple open tabs/windows, and we just multiplex all incoming websocket events to all connected "frontends". I agree passing messages is a bit clunky, but we’ve taken a similar approach to comlink so we can simply "await" a function call on the backend from the frontend. Besides setting that up once (or just using something like comlink), it's not much work. Okay except I found debugging on Chrome a bit clunky, as I think Firefox allows you to see all SharedWorker console output from within the main thread console for example (with Chrome you can open the inspector for shared workers separely). Plus we also have all kinds of other neat features with modern browsers these days, like the SharedWorker, and zero-copy communication using Transferable objects. [1] https://thymer.com/ https://thymer.com/