Emerging Rust GUI libraries in a WASM world
monadical.com
monadical.com
WASM demo here if you want to see it (with dummy, and not particularly realistic, profiling data): https://elliottslaughter.github.io/legion-prof-viewer/
For comparison, the UI I'm replacing falls down with about 16 nodes worth of data. (The demo has 8192 "nodes" worth of fake data.)
I sincerely hope the web as a whole doesn't move in this direction. The inspectibility of the DOM is one of the web's greatest strengths. Lets not throw that away.
And input element behaviour is non-native, which will never be properly fixable since different UAs have different behaviours, some of which cannot be emulated even if you sniff which platform you’re on (e.g. scroll bar grab and drag behaviour when you move far outside the window). (Then there are also simple bugs: holding Left or Right while the range element is focused only bumps the value once, not according to key repeat. And Up and Down don’t work.)
I have written about these things a number of times, fundamental limitations of the pure-canvas approach that make it unavoidably and permanently unsuitable. https://news.ycombinator.com/item?id=33861831 is probably the best thread.
(And just for a bit of fun: the demo can have slightly negative utilisation, drawing outside the box!)
I wonder if your experience is different?
As best I've been able to tell, right now you have to make a choice between (a) fast and (b) native, and you only get to choose one. And in my application domain, fast is nonnegotiable.
Smoothly rendering 3k rectangles isn't a common use case for UI libraries, so I'm not surprised they perform poorly. They generally aren't optimized for that. The normal answer for weird, high performance rendering like that is to just use opengl or something and program against the GPU directly. 3d rendering contexts can render millions of rectangles on modern hardware without breaking a sweat.
This advice is the same on desktop, mobile or the web. I'm not surprised rust UI frameworks have the same limitation. Most UIs don't need thousands of rectangles. But they do need lots of custom widgets, and implementing all of those well is a higher priority job for anyone building a custom UI library.
Going to 3d rendering via OpenGL / WebGL was one of the options I considered. But honestly, this is a boatload of complexity. Why do I need to pick up a full 3D rendering library just to draw rectangles on the screen? Plus, consider: the slot viewer (where the rectangles are shown) still needs to draw some UI components (e.g., on hoverover). I still need that UI integration there, even if it's not as performance-sensitive. So I end up needing something along the lines of egui anyway.
I definitely understand that not all UI libraries are going to be optimized for this. But I was surprised that it wasn't easy to e.g. get a high-performance canvas dropped into libraries that specifically advertised themselves as high-performance.
By limiting yourself to a 2D engine you're ensuring there are more layers of abstraction between you and the hardware. While # layers of abstraction doesn't always correlate with overhead, it does place limits on performance.
It’s still a bit of a mess though. I understand wanting to keep everything in a single UI library if you can.
Example: https://share.firefox.dev/40QUthv . (Though this trace is probably smaller than the profiling traces you're demoing.)
Maybe you have graphs/charts that need that kind of rendering effect. Great, use Canvas for them, they're not accessible anyway. But if something like your main user controls are running into that problem, I feel like something has probably gone wrong with your user interface.
In the demo you linked, it makes a lot of sense to render to Canvas for the individual charts, flame charts, breakdowns. It does not make sense to me to render to Canvas for the sidebar. It doesn't make sense to me that the list element itself holding the charts is going through Canvas. That's something in your UI that honestly shouldn't be updating at the speed you're talking about.
To be honest, I'm not sure Canvas can handle either part of this: either drawing 80k rectangles at 60 fps or culling so that the other 10 billion don't need to waste CPU cycles. It requires a very tight integration with the UI library to make sure we do this fast.
Yes, it's a very narrow use case. That's why I went with a specialized UI library.
To be clear, WebGL/WebGPU is still using Canvas. You're not using the 2D api stuff, but that's just a high-level API on top of Canvas. You're still rendering to Canvas.
> I don't know if you noticed, but my test has on the order of 10 billion rectangles
Why? I've built apps like this before. Your choice is not "abandon the DOM" or "have zero performance." You can have most of your interface in the DOM and use Canvas (again WebGL/GPU is still Canvas) for specifically the parts of your app that wouldn't be accessible anyway (mainly charts).
It does not require tight integration with the GPU to render the sliders on the left hand side of the screen in this demo. No one is saying that you need to render your charts in DOM. We're wondering why you got rid of `ul` elements.
I didn't test Canvas specifically in my explorations, but I did test a few JavaScript based frameworks, and the ones I found that were efficient sat on top of WebGL, not Canvas. Ultimately I didn't pick those because they had a bunch of other stuff baked in that I didn't need or want, and were already equally non-native (sitting on top of WebGL). And I frankly didn't want to be writing JavaScript.
You are using Canvas right now. WebGL uses Canvas to render to the screen. If you open up your demo's DOM inspector right now, you will see a canvas element. It's:
<canvas id="the_canvas_id" width="1914" height="580" style="width: 1914px; height: 580px;"></canvas>
It's a little bit hard for me to trust this kind of dismissive talk about the browser adding too many abstraction layers when it's not clear to me that you're fully aware of what browser features your demo is using right now. Targetting the DOM and using embedded canvases really doesn't have anything to do with whether or not you're writing Javascript, and it doesn't mean you can't use WebGL to render charts.Ehhh sort of. Canvas is much older than webgl. When we talked about canvas a few years ago, that always referred to the canvas DOM element and its associated 2D shape based API. (fillRect / moveTo / lineTo / etc). When Webgl appeared, the nomenclature was "using canvas" vs "using webgl", since webgl's API is completely different from the 2d canvas API.
But browser manufacturers didn't give webgl its own DOM element. Webgl also uses a canvas DOM element, then configures it differently. (getContext("webgl") instead of getContext("2d")). So if you're using webgl (which I think this app is doing), then you're "using canvas" in the sense that your HTML contains a <canvas> element. But you aren't using the canvas 2d API - which, as I said, is totally different from webgl and is also called "canvas" because it was here first. And it doesn't have another name.
In defence of the GP comment, I think its pretty clear from context that "Canvas layers on top of WebGL/GPU draw calls" refers to the canvas API, not the canvas DOM element.
If it's just a context misunderstanding of what specifically "Canvas" is referring to, fine, I don't mind clarifying what I mean by that. And you're right, it's not unreasonable for someone to see the word Canvas and think "2D APIs." But I just want to be very clear that targeting the DOM for UI controls doesn't mean you can't use WebGL or WebGPU and direct graphics programming for charts, or that you can't render a chart in a web worker and then use the pixel buffer with a canvas. And none of that stuff requires you to program in Javascript (except for glue code, which you'd have to write for all of those methods no matter what). In modern web terms when I talk about targeting the Canvas, I just mean rendering pixels onto a canvas element as opposed to manipulating the DOM, regardless of how or where those pixels are calculated.
There is no additional set of abstractions you need to go through to do that, and there's no graphical system for the web that gets rid of the singular abstraction of needing to eventually put the pixels onto a canvas element.
Yes, if you were calling browser 2D APIs, that would have an additional performance cost, definitely. In a context where that's not performant enough, then don't do that? But there seems to be a suggestion in this comment thread that embedding canvases would require working with a higher level more abstracted graphical API when rendering the charts, and that's just not true.
Can you recommend an alternative to egui that let's you write entirely in Rust and get the performance of egui but still maintain some level of accessibility?
Ideally yes. This is a failing of the egui toolkit, not necessarily a failing of the people using it. The whole point of a high-level toolkit like this is that you shouldn't have to worry about how things are being rendered.
However, it is still important to counter claims that egui is making if the people using the toolkit are parroting those claims.
> Can you recommend an alternative to egui that let's you write entirely in Rust and get the performance of egui but still maintain some level of accessibility?
I don't think I've ever built a web GUI in Rust so I have no idea what the ecosystem looks like. To my detriment, I tend to build most of my UIs from scratch. I can promise that it is possible to have performant UIs with a lot of embedded charts (like this demo has) using the DOM, but I have no idea what Rust framework devs have been getting up to or publishing.
There's also a little bit of a catch-22 here because yes, any time you introduce the DOM, you are going to lose some performance. That doesn't mean that you're going to lose so much performance that the demo above would be stuttery or wouldn't work. But if you're just measuring "how many quads can I refresh on the screen", that's kind of missing the point that you really shouldn't be refreshing thousands and thousands of quads of information in a UI at 60fps. That's not a thing that most interfaces should be doing in the first place.
And if you are genuinely changing thousands of boxes of information on a screen at the same time, kind of by definition that isn't accessible period. There is no way to present that volume of informational change to a screen reader in a way that will be understandable. But a lot of people when building UIs tend to think they're in the rare cases where they need that level of performance and so they need to sacrifice that accessibility, when in reality what they actually have is a giant list of charts and aside from the actual chart rendering, there's nothing particularly expensive going on.
eg. I used to be able to programmatically grab hWnd's (like Spy++ does), and manipulate the forms of an app. Apps used to show up as one (or two) simple processes with rich instrumentation metrics in tools like Resource Monitor.
Chrome abstracted all that away in an effort to 'displace' the OS.
I think canvas-only technologies could eventually do the same (the endless cycle continues) and look forward to some of the benefits (eg. more deterministic and straightforward positioning and layout). But I strongly agree they need to accommodate most of the features you mentioned and until they do it's two steps backward.
Do appreciate your thoughtful comment and the concise summary of some fundamental limitations that are presently hard barriers.
eg. I used to be able to programmatically grab hWnd's (like Spy++ does), and manipulate the forms of an app. Apps used to show up as one (or two) simple processes with rich instrumentation metrics in tools like Resource Monitor.
---
I disagree pretty hard with this sentiment. Yes, it's harder to programmatically interact with some of the browser features from outside of the browser. Programmatic interaction with basically any application that isn't yours is getting harder across every operating system as security becomes more important.
That doesn't really change that introspection of the source that makes up a displayed site was easy and approachable. I can look at the css/html/js. Further, all of those resources are contained in files intended to be opened and looked at by humans (they're human readable text).
This is my biggest issue with WASM. You're literally back at reading assembly again. And while that's possible, and there is tooling that can help - it's just not the same as being able to right-click on something, alter a couple of attributes in human readable words, hit enter and see that change.
WASM on its own isn't the problem. WASM just lets you use another language to replace javascript. When you right click on something in a browser and alter human-readable attributes, you're usually messing with CSS and HTML. Any half decent web UI framework should pass HTML and CSS to the browser in order to render the UI. Thats just as true with wasm frameworks as it is with javascript frameworks.
The problem in this example is that the wasm code is bundling its own UI library that is trying (badly) to replace the browser's layout engine. All the browser can see is a bunch of drawing calls to an html canvas. Thats not the fault of wasm. Its just this UI library, which isn't really designed for the web.
For what it's worth, egui does have an accessibility layer. I haven't gone to any particular effort to wire it up yet (besides what you get by default): https://github.com/emilk/egui/issues/167
So what are you doing that couldn't be performant in the DOM? Because any kind of fast updates that you're doing with a lot of triangle that are too fast to render into an XML tree are also going to be happening too fast to describe with a screen reader.
The DOM is asking you to be able to describe your interface -- performantly -- in entirely pure text. If you can't do that, then there's no way you can be fully accessible, because the screen reader needs to be able to describe your interface -- performantly -- in entirely pure text.
If you are updating thousands of rectangles in your UI at 30/60fps, it is impossible for you to fully convey the same amount of information that is changing visually to a screen reader.
Either your interface can be described using pure text, or it can't. Either I could use your interface by closing my eyes and having someone describe it to me, or I can't.
Accessibility could be fixed easily if there would be a web accessibility API. There's basically an entire 'middle layer' of web APIs missing, current APIs are either too low level (like WebGL/WebGPU) or too high level (like the DOM).
i mean, it sucked, but it was kind of that middle layer.
It does have one. The DOM is the web accessibility API. A really big point of the DOM is that you don't get to make multiple interfaces, you have to make one interface that works for everyone, which forces you to provide some degree of feature parity between those interfaces.
The web accessibility API is "you have to describe your interface to us in pure text, because that format is more likely to be accessible."
I'm not happy about it, but I think it will.
> The inspectibility of the DOM is one of the web's greatest strengths.
Not if you're pushing ads.
Egui slaps and is clearly going places.
I've run it before. I know what it looks like.
I use egui. It's OK. But you have to code up all your dialog boxes in Rust. This is misery if you have fifty dialog boxes and widgets you need. The widget library is weak and the themes are poor. I don't care, because I'm doing a metaverse client, and all the visuals are in the 3D image. The 2D GUI on top is minimal, as is normal for games. So minimal that it disappears completely, like YouTube controls, when you're not using it, to give you a clean 3D world.
(Egui hint: don't use bottom-up layout. Always work top-down, left to right. Egui layout is one pass, and while it tries to look ahead, it's not good at it.)
It's certainly nice to work with from a developer's perspective, what could be easier than
if ui.button("Say hello").clicked() {
println!("Hello!");
]
?But it's quite a pain to style if you're particular about typography and pixel-perfect layouts. Between styling an egui app and styling something with CSS (or some flexbox/grid system), I'll take CSS every time.
Still looking forward to how Xilem turns out, and what people build on top of that.
https://github.com/leptos-rs/leptos
These are already orders of magnitude faster than React, and as the WASM<->DOM bridge improves, they will only get faster.
Sycamore:
https://github.com/sycamore-rs/sycamore
https://sycamore-rs.netlify.app/examples/todomvc/#
Dioxus:
In about 3-5 years these frameworks will start to consolidate and reach maturity.
It's already possible to build reactive isomorphic apps in Rust.
If you haven't checked out Actix/Axum, it feels a lot like Python/Flask. The ecosystem is coming along quickly.
Electron builds native software by embedding a web browser and using the browser's layout engine and DOM. Canvas based rendering like this is the inverse. Here the application discards the browser's layout engine and DOM, and instead embeds its own layout engine. - Despite the browser being right there and available.
Hilariously, in a demo like this there's 3 layers of UI controls nested inside each other. First, there's the OS's native UI toolkit. Then there's the browser. The browser discards most of those UI elements, and reimplements its own controls. And then Egui discards all of that and embeds its own, third layout engine all written in wasm. No inertial scrolling. No web inspector. No native controls (especially a problem on iOS or android). No CSS - so I hope Egui's layout engine supports the layout you're after.
Downside: Its a 5mb wasm bundle. But on the plus side, 5mb is positively tiny compared to shipping electron!
Egui only use wasm in situations where you must use the user browser because you have no choice. Eg: showing a demo of the app on the web without having to install anything.
> Its a 5mb wasm bundle. But on the plus side, 5mb is positively tiny compared to shipping electron
This comparison makes no sense. Situation where electron could be used are exactly the kind of situations where a wasm bundle would not be shipped (a native binary would be shipped instead)
Sure; but given the insane size of shipping chrome, it’s fun seeing a full app complete with widget library, layout engine and text input elements in 5mb. It’s terrible to make websites that big. But if chrome could do all that stuff in only a 5mb binary then I’d be much happier with electron.
If you didn’t catch it, the wasm bundle is running in a 32mb memory slab. Positively slim by the standards of modern gui apps!
Next I clicked on the combo box, it opens the dropdown, and I try to use arrow keys to navigate it. That doesn't work. Is that because I opened it using the mouse? Nope, still doesn't work if you tab through the widgets and use Enter to open. Looking up items by typing the first few characters doesn't work, either.
This is all really basic stuff that native widgets offer to any app for free on any platform.
I don't like how those textboxes get bigger when you click into them.
Would it be faster than iced or Slint at rendering scrollable list with thousands of rows with images and text? For the sort of use case you’d use a “virtualized list” implementation for React for example.
I guess that egui works similar to Dear ImGui: only (font) texture changes and clip regions (e.g. scroll areas) require a new draw call, and that's typically just a handful to a few dozen draw calls even for complex UIs.
For long lists, Dear ImGui has a special 'ListClipper' class which allows to iterate over the visible 'slots' of a list, so that the code which describes the list UI can skip clipped items early. Not sure if egui has something similar.
An egui ScrollArea can request only visible rows from a callback using show_rows:
https://docs.rs/egui/latest/egui/containers/scroll_area/stru...
Btw: when clicking any 'task' to get to see the details, I see a repeating pattern of almost vertical stripes in some colors. I guess that's not what it should look like.
That may not have been clear, but I am talking about drawing less nodes in any case, also when using egui.
In any case, it looks like a good base for further optimisation to ignore offscreen UI elements early (e.g. not sure if egui has something like Dear ImGui's ListClipper).
I bet the canvas approach would not slow-down the notebook.
Alternatively, try pygfx for ThreeJS graphics in Python leveraging wgpu. It works great in Notebooks through notebook-rfb. https://github.com/pygfx/pygfx
If you're adventurous, figure out how to make pygfx work with webgpu via wasm
https://github.com/bokeh/bokeh
I did do a basic test, and the raw rects-on-screen performance is roughly comparable to my final solution.
Some other ideas:
- Temporarily stopping the rust-analyser server to avoid lock contention on the build dir.
- Using cranelift codegen backend (not sure of it's current status).
Re: cranelift, I gotta check that out! Thanks for the reminder!
[0] https://github.com/rust-lang/rust-analyzer/issues/6007#issue...
areweguiyet.rs was started almost exactly 5 years ago. That means it's been about 60 mortal years and we still don't have a definitive solution to GUIs.
And guess what? Browser does not have a fuck ton of well supported widgets. All it does it some button with outdated UI, few inputs nobody really uses and dropdown select suitable only for the most basic uses.
Anything other built on divs with CSS and JS.
And it works.
So my opinion is that it's definitely solid foundations what matters. Rest will come with time from third party libraries.
Adobe Flash anyone? Silverlight? Before WebAssembly/WebGL/WebGPU unity (yes that game engine) even required a browser plugin. Web browsers were often severely incompatible with each other and web standards were a suggestion at best. Now, it's bad form to slander dead people so I will just say that the nice kind folks at apple under the orders of their dear leader did their best to destroy as much of the pre-2012 web as possible by gatekeeping the iPhone browser and using their market power to strong arm web technology.
The problem is we’re in 2011 again, except chrome is the new flash and safari is the ie6.
I beg to differ. When the economics of doing UIs shifted away from APIs like Windows Forms, Carbon, JavaFX towards the current trend of doing everything in a browser, it brought with it some serious regression in how powerful and user friendly those GUIs are.
With Microsoft-level resources, you can take on a mammoth engineering task like implementing Office or Visual Studio in the browser. But if you are resource-constrained in the way that most developers are, you cut corners and drop functionality, and that's where we are today with browser GUIs ...not that any of that matters if all you're doing is CRUD, like most people are. But one shouldn't judge technology by its easiest, most boring, and degenerate use cases.
Consider, for example, the evolution of a CRUD app that was first written in, say, the late 80s for OS/400, using an interaction style where it's all full screen forms rendered as text that you fill out with your keyboard, then submit. Say, you rewrote that app in the 00s in Windows Forms but using the same interaction design, and then again in the 20s as a browser-based app. If you look at the evolution of what happened to UI technology through the lens of that app, you won't feel like anything is amiss in 2023, but you're also kind of missing the point of having a GUI in the first place, as opposed to, say, a text terminal.
Whenever I hear "all I need is a textedit and a button" from a web developer, I kind of assume that this is what informs their viewpoint.
If you instead look at the evolution of UI technology through the lens of an app like Photoshop, it will immediately be obvious to you what it is that GUIs uniquely have to offer that, for example, text terminals can't, and why rewriting such a UI in a browser is anything but trivial.
The GUI part was started by bootstrap and similar tools and shifted to React, Vue etc.
What are you talking about ? HTML and CSS is full of widget libraries, it's the best cross platform widget library out there.
Ease of deployment + reach is the driving force behind improving the platform, but at the present nothing in Rust can even compare to something like Material UI. And let's not even go into stuff like date range picker components and shit where companies probably spent millions in engineering effort to get them right (eg. AirBnB) .
That's... entirely wrong and not necessary to paint swathes of developers like.
Rust has numerous solutions for native GUIs. People ship apps with those stacks. Rust also has things like Tauri for when you don't want to deal with differences across platforms.
Just because there's not one blessed solution doesn't mean it's not possible to write UIs in Rust today. ;P
Egui is mostly bindings. And those can suffer from impedance mismatch (e.g. using OOP UI in Rust)
I'm excluding games because they tend to have trivial GUIs. I'm excluding tauri because the majority of the GUI code in a tauri app isn't Rust.
This is a serious question. I'd love be shown I'm missing something. I read this article hoping to find out that a serious rust GUI library was ready for real use and was quite disappointed.
So you end up with stuff like the image crate (a straight up disaster of a crate—lots of fancy Rust bells and whistles, doesn’t support a bunch of stuff you want to do with images, reading the GitHub bug reports make me feel no hope that the issues will be addressed, ever) and R (a straight up disaster of a language, just a nightmare to write, but full of useful statistics packages, always has the package you need, but the language was made by Satan).
I’ll say the same thing about R+statistics as I will for Python+machine learning, for C++&game development / OpenCV, or Fortran and scientific computing. Having domain experts actually use your language to solve problems is such a massive advantage that you can almost ignore the benefits and drawbacks of the language itself. Almost.
You might only want 8 colours, but if you don't know exactly which 8, you'd better have a large palette available.
In the very rare cases it's not it is better to build a custom widget that matches your customers requirements exactly. Nothing is worse than a half-baked GUI element idea, badly implemented and sloppily maintained that matches like 85% of your customers requirements - and that's what most non-standard widgets are.
Quality and consistency trump quantity in my opinion.
Just to say.
But I agree with you: if your work balance is more oriented towards productivity then rust will not help much. For example, I design lots of stuff in python/jupyter/ecosystem first and then port that to rust for the speed and correctness.
For any app more complex than a demo "todolist", handling "business logic" is - by far - the most important. Hundreds of widgets that don't do anything, or do it wrong and buggy, are worthless.
"handling business logic" is difficult and, ironically, perpendicular to many frameworks (as in: frameworks make dev/testing/evolving/refactoring of business-logic harder, not easier). A perfect example was (is? IDK) Visual Basic: tons of widgets, a neat builder, but terrible in managing even simple state and handling business-logic. Or React, which is a perfect framework (and paradigm/architecture) for small, or flat apps, but terrible for complex or convoluted apps.
What I see in Rust, is a focus on the stuff beyond mere "providing lots of stuff that can be drawn to screen": how to manage state, react to changes, manage events etc. Which is, the way I see it, why there also are so many frameworks emerging: what architecture and paradigms work best, very much depends on your use-case.
That said, it feels like it's starting to change. Lower-level components are getting stabilized, SlintUI and iced are picking up speed, etc.
1. There are unprincipled Rust GUIs, e.g. Egui. It works great.
2. Making a GUI framework is just bloody hard and a ton of work. I think only a small handful of languages have native GUI toolkits. Most just wrap C or HTML.
GTK is 25 years old and Qt is 27 years old. I don't think 5 years is long enough to develop a mature GUI toolkit (unless you have an enormous company backing you maybe).
Finally, modern GUIs are way more complex than they were back then - especially when it comes to graphics APIs and text handling.
So I still think it's too early to say Rust has failed when it comes to GUIs.
WinForms used to be the default for Windows, Tk was the default for Linux (or at least that was my impression, I was all in on Windows back then), etc.
For many people, using web tech has become the default if you want to build cross platform UIs. You have the option of building with imgui, Qt, Gtk, etc. but I don't think it's controversial to say that your average dev who wants to make a cross-platform GUI will probably use Electron or an Electron-like.
I really want to have a default for the Rust space. I've even done some work here myself which is why it's surprising to me that we aren't there yet
Java might perhaps be the best example of trying to make a default-for-a-language cross platform UI (Swing and whatever it was called that came before it). But I think it's also an example of why it might not be a great idea to even try.
I think a key realization is that an app like Blender or AutoCad need a different UI paradigm than Spotify or a game overlay does, even on the same platform there are differences there. Apps that can use web based UIs tend to be more like Spotify than Blender...
The API is a bit old now, but you could easily put a reactive layer on top.
Speaking as a Rust user, everyone in Rust land loves insulting Electron, but they have put some solid work into accessibility. In particular, using WASM to draw text with webgl or canvas, instead of DOM, may in the long term be one of the biggest steps back in accessibility ever.
I still keep my comment about the article -- I feel discussions of GUIs should talk about this type of thing, as so many people require accessibility support.
I know Slint and egui have accessibility support.
(but hopefully it'd be a temporary step back)
The pure canvas approach used by things like egui (and it probably can’t change it) and current iced (it used to have iced_web which rendered to DOM, but that’s been abandoned for now for no obvious reason) is fundamentally and irredeemably unsuitable for general-purpose web content and apps, in ways that things like AccessKit cannot fix. I’ve written about this a few times; https://news.ycombinator.com/item?id=33861831 is probably the best thread. The two items I like to focus on are links and scrolling, both of which are unavoidably broken.
So I usually produce pngs via matplotlib and only selectively use interactive, js based, visualisations but they are get so unbelievable slow quickly when that your whole tab freezes.
For individual widgets like data visualisation, go ahead, use canvas if you want; I won’t complain—though make sure if you have any links in it that you have actual <a> links on top of the canvas for the user to interact with. And also consider if you can SVG. Anyway, individual canvases are fine; but don’t put everything in a single canvas.
Things like Google Docs and Figma are often cited as examples of how canvas-based rendering can be not bad, but neither are pure canvas: the user interface of both is full DOM, and it’s only the document area that’s canvas. (Also, I find Google Docs somewhat unpleasant: its rendering latency and throughput while you type is terrible, and keyboard caret navigation doesn’t match my platform’s behaviour in many places.)
See also https://news.ycombinator.com/item?id=33863185 (similar content to this comment).
That being said, yeah, there are people in this very comment section saying that of course egui is accessible, it supports AccessKit. So it's not really theoretical, any attempt to close the gap with AccessKit is going to be used by people as an excuse to build inaccessible applications. That's already happening in this comment section right now.
But that doesn't mean that AccessKit shouldn't exist it's just... one of the downsides to reducing the number of entirely inaccessible apps. It's a partial fix for a bad situation, and it's good for devs to use it if they have to, but it's far better for devs not to need to use it.
AccessKit is trying to help fix a problem as best it can. My issue is with the people and frameworks that are causing the problem.
The web does have an accessibility API. It's called the DOM.
What developers are upset about is that they have to make their apps performant in that accessibility layer. The DOM restricts you from developing a fast app for sighted users and a much laggier less-featured app for users using the accessibility API, because it forces you to always have the accessibility API turned on. It forces you to treat the accessibility API as your default rendering target (and allows you to occasionally make something inaccessible by embedding a canvas element inside of that UI for one or two parts of your app.
And if you can't develop a fast app using the accessibility API, then having a toggle to turn that layer off is not really a solution for that problem. I'm very glad AccessKit exists, it's better than nothing, but that doesn't mean the apps that are using it should be proud of themselves.
Browsers seem to have resisted adding fundamental alternatives of construct-on-query accessibility trees, or some kind of “I need AT stuff” switch so you can skip setting ARIA attributes or materialising other DOM if it’s not going to be used.
The end result is that the web doesn’t properly support accessible virtualised scrolling (where you have a hundred thousand emails in a mailbox, but only render the ones on screen, still providing a meaningful scrollbar) or infinite scrolling. There’s role=feed which improves matters in most screen reader configurations, but it’s still making some compromises. But then, virtualised scrolling is imperfect even for non-AT use—things like browser find-in-page won’t work properly. Still more of the “if you want it to work properly, it has to exist in the DOM all the time” stuff.
To be honest, AccessKit itself doesn't support lazy accessibility trees for virtualized scrolling, at least not yet. It can avoid constructing the accessibility tree completely if the platform accessibility API isn't queried at all, but there isn't yet a way to only partially construct an accessibility tree while indicating that there's more available on demand. So to some extent, I guess AccessKit brings this web performance problem to native.
That being said... I have also kind of soured on virtualized scrolling over time as I've spent more time working on UX projects, and I realize that sort of sounds like an excuse (we don't support this well, but you shouldn't be doing it anyway), but... I do kind of feel like infinite scrolling and giant lists of elements to the point where the list needs to be virtualized is bad UX for most apps.
----
And to be clear, I'm not necessarily saying that the DOM is perfect as an accessibility tree, just that it's good enough compared to the kinds of accessibility APIs that most native devs want when they complain about the DOM. In practice, a full-featured accessibility API with the same feature-set as the DOM and the same level of capabilities would likely have performance costs no matter how it was implemented in the browser.
Sure it could be better. But also all of those improvements could just be added to the DOM instead of making a new native web API. The biggest difference between adding those improvements to the DOM vs reconstructing something very similar to the DOM as a separate browser API is that devs could ignore the performance costs of that API and only benchmark their apps with the accessibility API turned off.
And I think it's good for devs not to have that option (or at least, for browsers themselves not to directly provide that option; again I'm not saying that I don't think AccessKit should exist).
If your app can be described to a screen reader, then there should be an easy cross platform API[0] for GUI toolkit makers to use, provided by the underlying platform in a reasonably simple way.
[0]: I am aware of AccessKit. Where's the OS backing?
I really don't understand the use-case for a Rust-based GUI.
Rust's syntax, manual memory management, and compile times makes it seem like it's really not meant for this kind of workload, but instead for things like compilers, embedded systems, that kind of thing.
I understand that not every UI needs to be a cross-platform blah blah blah written in TypeJavaReactScriptJS, or a pixel-painted canvas written in Flutter. I also understand there's a use-case for one-off UIs that may not target a browser.
Let's compare it to something like Golang. Fast compile times, similar libraries, garbage collection -- it seems like something more suited for UIs where there are usually a lot more iterations. Worse performance for sure, but Rust also seems more-than-overkill in that regard for the UI use-case.
We also find that once you get used to the borrow checker, we mostly don't miss garbage collection. And garbage collection isn't a panacea either, as almost any large project still ends up with memory leaks caused by stray references.
So, basically, we like Rust and think its a well designed language. It is quite frankly better, as a language, than any of the alternatives that we could be writing in. As such, it'd be nice if we could develop GUI applications in it.
Any Javascript devs will probably hate working like that because now they're forced to write types for absolutely every single thing, but to me it felt like Javascript was finally fixed.
Just banning things like "any" and enforcing proper nullability makes up for a lot of Javascript problems.
- setup linting (which is awful and annoying) - formatting - use typescript
and then maybe u have a usable work environment. Rust just does that out of the box.
Even with Typescript you're dealing with the disastrous state of NPM/configuration/linters/formatters/libraries -- all of which are firmly Javascript-esque plagues (though not exclusively Javascript plagues).
I looked into Rust for writing games (including the UI frameworks), but found that the power it provides isn't really worth all the mental overhead. Most games don't really need that much power, particularly if you are using non-realistic graphics. In the event that you do actually need extra optimization, most engines let you hook down into C++ for raw performance.
The syntax isn't even close to being a difficult part of the language, and it's something you get used to very quickly. The Rust syntax just isn't readable for people who don't yet know Rust, just like I find C++ difficult to read.
> You can just get much more done, in less time with a web framework.
Sure, you can also get it running even quicker in Python. But when it comes to maintenance I prefer something where the type system wasn't just slapped on top. That said the web does feel like the only fully-featured and truly cross-platform GUI at the moment.
I guess if I wrote / read Golang sources more frequently, that would become a second nature and would not slow me down.
No it is not. Or at least not for everyone.
I have learned several languages more unfamiliar to me than Rust (pure functional, logic, stack-oriented,etc), but none has given me the difficulties Rust has. Also (only my sample of course, but there are no reliable stats) no-one I personally know who has gone through the Rust book has continued with Rust. The Rust Foundation hasn't made 'flattening the learning curve' one of its 2024 goals because people don't find Rust harder than other languages. Many just do.
A false universal doesn't become true through brute repetition.
My problem with using Rust is the way its complexity creeps into everything. There are large numbers of stdlib utility traits whose idiomatic use you have to remember to read almost any real world code. There's hardly a Rust library in common use that isn't a huge sprawling mass of over abstracted generics and macros. Even command line parsing is made to be a vastly complex endeavour. There are admittedly sugary niceties (eg derive attributes) that can make use of many of these libraries tractable for easy cases. But overall any substantial Rust program makes cognitive and memory demands on the programmer far in excess of what most languages require for the equivalent in my experience.
> Even command line parsing is made to be a vastly complex endeavour.
Yes, I even wrote a library for that once but then realized it only complicates everything and adds limitations. Nowadays I just do that by hand. For one it's guaranteed to be shorter than any parsing library, but also it's a good opportunity to think about what kind of arguments I need and in which form. And copy-pasting the parser from the previous project and adapting it is quick & easy.
My beef (such as it is) is that I doubt I will ever get to the level of fluency where I 'just code' with it, rather than intermittent stripes of coding & fuming. It takes me probably 4x as long to do anything compared to any other language I've used (and although I'm not a talented programmer, I am a polyglot).
Oddly on the cli parsing thing, I did end up finding a library I like - bpaf. It's kind of abstract but seems more conceptually principled than the near-universal clap. The kind of abstraction where once you have grasped a small set of orthogonal concepts, you can just combine them at will, seems OK to me. As contrasted with the type which has dozens of small rules, and special cases that rely heavily on (human) memory and/or constant doc lookups.
I know what you mean. I feel much more productive with it nowadays and many things just make sense now, but it took me a long, long time to get to that point. Rust is very much not beginner-friendly, and while I think it was worth it this is why it will never replace certain other languages. I don't mind the compilation speed as much as some people seem to do, but the learning curve definitely is a problem.
However I can imagine when Rust borrowing rules can be a huge pain. This happens when you expect to continue using the same style of programming that relies on big piles of (often cyclic) object graphs built with references/pointers, shared mutability, heavy (runtime) indirection and inheritance. A style that is so prevalent in OOP projects written in Java, JS, Python. I don't like this style at all, and always tried to avoid it even when coding in those languages, because I believe it causes more harm than good. Rust will simply force you to unlearn it. And some people won't need to unlearn much, and some will have to unlearn a lot. Changing habits is always pain.
I think it should be simply considered a different paradigm.
I addressed this above and believe it to be an unconvincing explanation of Rust's difficulty. Many polyglot programmers comfortable with, and experienced in, switching (and learning new) paradigms, still find Rust more difficult than other languages they have learned. In my experience most just give up. I haven't, but neither have I become remotely productive in the language.
Of course my experience is just one person's sample. There isn't much else to go one. But the Rust Foundation clearly agrees with me, given its 2024 roadmap. In our view, Rust is hard to learn and use, more than most languages, and problematically so for many.
Also, there aren't any other mainstream languages with borrow checker at the moment. The problem with Rust is that it introduces many novel ideas, not just some old ideas wrapped in a different syntax (e.g. like Kotlin or Swift). They need time to get into developers heads. Similarly it took many years to accept some ideas from FP into mainstream languages, and people were even debating if Java needed lambdas.
And BTW: I didn't find Rust harder to learn than, e.g. Python or Scala... Scala was kinda fun, but FP required similar amount of mind-bending as lifetimes in Rust, and Python was just purely frustrating.
Because these two lists are mutually exclusive.
On the survey they ask:
* Do you use X today?
* Do you want to continue to use X in the future?
"Most loved" means "the ratio of yes/yes answers is high", and "most dreaded" means "the ratio of yes/no answers is high."
Rust wins "most loved" because many users say yes/yes, and few say yes/no.
It is very hard if even possible to objectively assess the difficulty of the language, because difficulty is inherently subjective and depends heavily on prior experience. The fact that there are many people who claim to be more productive in Rust than e.g Python or Java, but at the same time many others that don't share that opinion, IMHO supports my claim this is mostly a matter of a paradigm shift.
Also I noticed many developers somehow don't count the difficulty to fix bugs towards the difficulty of the language. And maybe that's also an explanation why we end up with so vastly different opinions.
The difference between what you and I are claiming isn't in the substantive content, it's that you, Shiny Happy Rust Person-style (exemplifying what many of us find so ugly and bullying about the much-vaunted "Rust community") insist on universalising your personal experience.
I've already told you that novelty is nothing whatsoever to do with why I find Rust difficult. It has few novel concepts, and they are simple enough. Note that I never mentioned the fucking borrow checker. Now fuck off. I've blocked you anyway.
For people already writing things (libraries, cli's etc) in Rust who want to add a GUI while staying with the language. No great mystery.
As for a business case, aside from individual developer taste/preference, it's hard to see that there is one outside of niches. System76 found a case for it with their DE. I think there might be a strong case for lightweight Rust GUI frameworks like egui for embedded device displays.
But for mainstream web / desktop / mobile apps? I can't at the moment see what the point would be.
What about a vector or image editor, a 3D DCC etc?
These are prime example for desktop apps where existing solutions are 100% written in C++.
That's stuff where Rust would be a better alternative if you started work on an app like this today.
In VFX there is a whole initiative by the ASWF to wrap the stack in Rust for starters and possibly have new additions written in that language instead of C++ in the future.
And compiling to WASM and being able to run in a browser (even if only as a single canvas element with all applying limitations) is a great plus.
However, this article also confirms an issue I have observed with the Rust ecosystem, which is fragmentation. It would have made me much more excited to see convergence behind one project than 15 different ones. I think Rust enthusiasts, bless their hearts, are often so excited about the puzzle of the language representations, that the output often ends up being more of a POC than a well-maintained library for users. It seems more-than-usual amount of projects are abandoned when the intellectually stimulating honeymoon phase is over. As a prospective user, this is probably fine for small leaf-dependencies, but it’s a major show-stopper for something like GUI.
I’ve never expressed it like this before, but I think there’s a moderately serious issue when a language seduces you away from the domain problem and towards the language itself, even if that is subjectively enjoyable. (In other languages like perhaps Java or C++, the language quirks are at least so dreadful that only masochists are spending more time than necessary on it)
Absolutely. The fragmentation doesn't matter with all library types, because they don't all need the vast array of associated libraries, tools, common practices etc which mainstream GUI apps lean on.
Wide GUI framework/library adoption needs a head of steam (far more than it needs architectural elegance). Right now I don't foresee this happening with Rust. Not everything is foreseeable, of course.
I have no doubt Rust will be used as part of many GUI apps via Tauri and others. 1Password shows how successful it can be as the real core of an app that has a separate web-tech GUI layer.
There's enough energy in the Rust ecosystem that I'm sure niche adoption will continue. But all-Rust apps beyond HN/Github-browsing/System76 circles? My guess is it's unlikely.
Only then we will know if it has chance of really becoming widely used as a general purpose PL.
If however you mean one used across a wide variety of industries, software types, and hardware, I think its future is already assured there. My own guess is that Rust will become quite ubiquitous for the performant and stable core of many software systems, with the interactive levels handled by other languages.
Larger bundle sizes - yes that's still true.
And not all GUIs are web GUIs, so WASM isn't the whole story.
Its true that large javascript bundles are bad because big files take longer to download. But the arguably larger problem is that parsing javascript is really slow. This isn't as big an issue with wasm - the browser can parse wasm many times faster than it parses javascript. If I recall correctly, wasm parsing speed is about on par with the speed of parsing pngs.
So having 335kb of wasm is similar to having a 335kb image on your landing page. Its not ideal, but its not a showstopper.
- A custom panic message (so you know which line of code crashed)
- Some monomorphized formatting code to output that message
- The infrastructure to generate a stack trace after a panic
- Logic to free all the allocated objects all the way up the stack
If you compile this 1 line function:
pub fn read_arr(arr: &[usize], i: usize) -> usize {
arr[i] // (equivalent to 'return arr[i];')
}
... You produce 20 hairy lines of assembler: https://rust.godbolt.org/z/dhz34KEvjIn contrast, the equivalent C function is this rust code:
pub fn read_arr_unchecked(arr: &[usize], i: usize) -> usize {
unsafe { *arr.get_unchecked(i) }
}
And predictably, the result is this gem - identical to what the C compiler outputs: example::read_arr_unchecked:
mov rax, qword ptr [rdi + 8*rdx]
ret
But nobody writes rust code like that (for good reason). You can get a lot of the way there by leaning heavily using rust's iterator types and such. But its really difficult to learn what patterns will make the rust compiler lose its mind. There's no feedback on this at compile time, at all.The things I reach for in practice are godbolt and cargo asm[1] - which can show me the actual generated assembler for functions in my codebase. And twiggy[2], which can tell you which functions are the biggest in your compiled binary and point out where monomorphization is expensive.
When I’m developing, I regularly run a script which compiles my code to wasm and tells me how the wasm file size has changed since the last time I compiled it.
Some tips:
Try to avoid array lookups with an index when you can. When looping, use slice iterators and when making custom iterators, wrap the slice iterator rather than storing a usize index yourself.
Be careful of monomorphization. If you’re optimising for size, it can be better to take a dyn Trait rather than making a function generic.
And play around with your wasm API surface area. It takes a lot more code to pass complex objects & strings back and forth to javascript than other types.
But otherwise, good luck! Love the project.
The problem is that it's real easy to just add a bunch of crates to an application, similar to the nodejs/Python approach.
Most people slso don't seem to turn off many parts of the standard library they don't, even for platforms like WASM. Maybe it's useful to have a stack unrolling panic handler during debug but in release you can just abort and save up to megabytes of space.
There's also a lot to be gained by tweaking the compiler optimisers. By default the optimizer is multithreaded, which makes compiles quite a lot faster, but reduce that to a single thread and suddenly a lot of optimizations can happen that wouldn't happen by default.
I wouldn't write code like described here in C, but I imagine Go and C# are better choices here. Maybe even that Java library the name of which I can never remember, or that Kotlin project that compiles Kotlin to Javascript with super easy interaction between frontend and backend.
I love Rust but if you're going to pick a systems programming language for your frontend, just make desktop supplications. Web is a nice fallback but if it's your primary target, there are so many better options out there.
Just for reference I was testing this the other day and compiled some simple C++ to WASM and adding:
std::cout << "some text";
to the code increased the binary size by like 5mb. Turns out std:cout pulls ALL currency-formating, date-formating and i18n code in the c++ standard library into your binary
ansi C printf does not meaningfully increase your WASM binary size
If you want your code to be able to be loaded on-the-fly and fast you need bundling tools, just like JS does. Bundling is a really hard problem, game devs struggle a lot with it as well (although their problem is usually bundling assets, not code itself)
If you're talking about Wasm with Emscripten, yes there's a cost of loading the runtime because Emscripten comes bundled with a lot of stuff.
I'm skeptical that just wasm itself was slower or heavier.
That doesn’t mean I don’t want Rust “people” to work on frontend stuff for Rust. Typescript is good, I like using it, but I’d like to have options. If not for anything else, then for Typescript to take the best parts of Rust and use them.
I don’t personally think we’re going to see a massive shift away from a JavaScript world until someone like Microsoft starts driving the change. The amount of resources they pour into Typescript means it’s not likely to leave my world any time soon. But if it did, and something better came along then I’d be happy, not sad.
I love typescript, but, from my PoV, what I miss in typescript is:
- An actually sound type system: This is a big one. Can you figure out why this[0] is unsound?
- I regularly have to do `as ` assertions, and I know it's not because I'm bad at typescript because it's often the recommended solution from cream-of-the-crop tS libraries for certain issues.
- Traits
- new-types
- Algebraic data types: Option and Result. 'nuff said.
- Pattern matching: :cheff's kiss:
[0]
export function useSetter<T, K extends keyof T>(
state: T,
setState: (n: T) => void,
key: K
) {
return useCallback(
(val: T[K]) => setState({ ...state, [key]: val }),
[key, setState, state]
);
}I understand the requirements of having a dedicated canvas and a decent UI running on it for things like games, CAD, 3D modeling and image manipulation apps. At the same time I don’t want my web bank using it. And unfortunately that’s usually what comes out from this.
For example, maybe you feel that VDOM is pure overhead and want to hide all frameworks that use it.
It's a bit disorienting to read many rather shallow lists with all the various frameworks without a proper convenient way to compare them
Is that not the case? It certainly looks nice, but is it ready for use?
This is another one that tries to attack the same surface area as rust but aims at being easier.
There is also Carp by the way: https://github.com/carp-lang/Carp
The situation (as I understand it) is unlike using a GC, autofree requires the user to have knowledge of memory management and how to use it properly. One of the objectives of Vlang is to be easy (easier) to use. Using a GC as the default, proved to perform very well with the language, and presented less complications for users.
It appears the strategy of the Vlang developers (as stated on their website and documentation) is to go with flexible memory management. That is the GC can be turned off whenever the user wants (-gc none), and other memory management options can be used such as autofree (-autofree) or their arena allocator (-prealloc).
This flexible memory management strategy would be somewhat similar to what Nim did, and interestingly, I don't see people crying as much about it. Furthermore, there is more to Vlang as a programming language than just autofree, as if its the only thing that counts. People use it for many other reasons.
1. https://www.youtube.com/watch?v=gmB8ea8uLsM (Autofree demo) 2. https://www.youtube.com/watch?v=mJG4Zg6Ekfw (Vinix OS)
fn apply<A, B, C, G>(mut f: impl FnMut(B) -> G, a: A) -> impl FnMut(&B) -> C
// must still be `for<'r> impl FnMut(&'r B) -> C`, because that’s what filter requires
where
G: FnMut(A) -> C,
B: Copy, // for dereferencing
A: Clone,
{
move |b| f(*b)(a.clone()) // this must do any bridging necessary to satisfy the requirements
}
all this did so I could write in my code ...
.filter(apply(second, i))
...Lots of people like Vlang, so better to accept that. Not everybody is going to prefer or think the same in regards to programming languages.