It’s definitely a lot to download, but comparing WASM and JS bundle sizes isn’t entirely apples-to-apples. Byte for byte, JS is one of the slower assets you can put on your page due to its relatively complex parsing requirements.
I’d be interested to know where the balance point is between parsing speed and download speed.
the tricky thing is that the balancing point would depend on the connection speed of your user. some people still have really slow 3G as their primary internet connection.
But a JS bundle can be cached, no? And I’d be really surprised if browsers aren’t JIT-compiling the cached JS internally into some kind of executable bytecode representation.
Yeah, it’s a bit chunky for sure but not a dealbreaker. It’s deployed as a PWA, which helps; it’s heavily cached until the user clicks an “update” button.
Didn’t read the article yet, but isn’t Go an awful choice for wasm because it brings a runtime with it?
Edit: read the article, justification was basically “we knew Go” (which is fine) but didn’t address the runtime issues. They spent a lot of time discussing memory issues, which again would be better handled in a non garbage-collected, non-runtime-having language, I’d think.
On paper, maybe, but the whole idea was to have n=1 languages for both of our UIs, and we weren’t about to rewrite everything in Rust. :)
The Go runtime seems to be “good enough” but there are some tricks to it, like configuring a hard memory limit in the client so Go GC’s before it hits the limit and crashes:
var memoryLimit = 1.7 * 1024 * 1024 * 1024
func AppClient() {
// Have Go enforce a memory limit so it GCs instead of crashing.
debug.SetMemoryLimit(int64(memoryLimit))
// ...
}
The memory challenges were more around fitting in WASM’s 2GB memory cap when viewing absolutely gigantic traces or huge amounts of logs. And also minimizing allocs in key places. These are both general optimizations that paid off on the TUI side too.
Did you experiment with TinyGo’s WASM backend at all? For large codebases it’s easy to run into its limitations but in cases where it can be made to work the resulting wasm can be much smaller, especially if you tweak things a bit.
It sounds like go-app itself would be a problem and considering the size and nature of the project my guess is that it probably wouldn’t work anyway but I am curious if you’ve tried and how far it got.
Yeah, I gave TinyGo a try but I kept running into missing packages/APIs and it was too troublesome to chase down all the broken dependencies (like gRPC). In the end I just threw Brotli compression at the problem and called it a day.
Still think the project is really cool though and would love to try it again in the future.
In addition to the heavyweight runtime and GC, Go’s syscall/js package seems to have been abandoned in perpetual experimental status as well (similar to their WebSockets package).
Unless I missed it, there doesn’t seem to be any mention of how it’s actually rendering things in the browser. With React/JSX removed, how are they bridging the cap between WASM and the visuals?
I’m curious about how they abstracted over both the web and the tui? Does go-app have a TUI backend? Or is go-app part of the web implementation and the abstraction layer sits above it? Also, I’m very curious how you build an application that works both on the web and TUI without compromising usability on one or both platforms? I would think that the two platforms are sufficiently different from a usability perspective that a single implementation makes for a poor experience on one or both platforms? This seems really interesting–would love to hear more about it!
The dagui package interprets our OpenTelemetry event stream and maintains a high level representation of a UI. The TUI code renders it to the user’s terminal using BubbleTea, and our web UI code (private - sorry) renders it with Go-App.
So, the abstraction is one layer down, not at the UI widget layer. Like you said, the terminal and browser have very different constraints and strengths - it wouldn’t really feel great to use what feels like a TUI in the browser.
I’m all for single source of truth and sharing code as much as possible, so it sounds like a great move! Some questions that I have:
What’s the accessibility story? You can go very far with just using HTML correctly, but headless component libraries like react-aria and radix go beyond that, fixing problems that can’t be fixed with only HTML.
Production readiness. Like one of the commenters, go-app reference crashed my tab. Is that acceptable for the business, “works best in IE 9” style, or does Safari have a bug?
Performance. 4.6 MB payload is big, but wasm is parsed while streaming so it may be better in practice. How does it compare to the system it replaced?
Code style. If I was coming from React I wouldn’t want to go back to dark ages by writing finicky selectors and raw DOM operations. Declarative is now what all web UI libraries are doing, and rightly so. I should check go-app later on my laptop, which would likely answer my questions.
Accessibility: AFAIK there’s no technical reason it can’t be just as good, but I don’t think I know enough about a11y to identify major gaps here. (I’m aware of aria tags for example but don’t have it internalized and probably need to do an a11y audit in general. Thanks for the reminder.)
Production readiness: hmm, well we’ve tested it across all major browsers (including Safari, desktop and mobile) and it doesn’t crash. I would guess that’s just a bug with go-app.dev. I will say that when the app crashes, it crashes hard.
Performance: it does take a second or so to load from cache, which now that you mention it does seem a shame considering it’s cached haha. In the grand scheme of things it hasn’t really mattered (no one has mentioned it), but it’d be nice to chip away at that.
Code style: yeah, Go-App is declarative-ish: you’re not writing HTML syntax like JSX, you’re just calling an API that’s codegen’d from the HTML spec. I like the DX, and the fact that it’s just code, but you do have to babysit parentheses, commas, brackets, etc.
Here’s v2 and v3 side by side if you want to kick the tires:
Isn’t that still a lot for frontend?
I think modern SPA frameworks have been getting a lot better at lazy loading things, right?
go-applooks sick though. It’s gona be on my to-try list now.It’s definitely a lot to download, but comparing WASM and JS bundle sizes isn’t entirely apples-to-apples. Byte for byte, JS is one of the slower assets you can put on your page due to its relatively complex parsing requirements.
I’d be interested to know where the balance point is between parsing speed and download speed.
the tricky thing is that the balancing point would depend on the connection speed of your user. some people still have really slow 3G as their primary internet connection.
And some may still prefer less data usage to a minor speed up
But a JS bundle can be cached, no? And I’d be really surprised if browsers aren’t JIT-compiling the cached JS internally into some kind of executable bytecode representation.
Spoke a bit too soon. It seems like https://go-app.dev/reference crashed for me on iOS + Safari. One big downside for using wasm.
Yeah, it’s a bit chunky for sure but not a dealbreaker. It’s deployed as a PWA, which helps; it’s heavily cached until the user clicks an “update” button.
Didn’t read the article yet, but isn’t Go an awful choice for wasm because it brings a runtime with it?
Edit: read the article, justification was basically “we knew Go” (which is fine) but didn’t address the runtime issues. They spent a lot of time discussing memory issues, which again would be better handled in a non garbage-collected, non-runtime-having language, I’d think.
On paper, maybe, but the whole idea was to have n=1 languages for both of our UIs, and we weren’t about to rewrite everything in Rust. :)
The Go runtime seems to be “good enough” but there are some tricks to it, like configuring a hard memory limit in the client so Go GC’s before it hits the limit and crashes:
The memory challenges were more around fitting in WASM’s 2GB memory cap when viewing absolutely gigantic traces or huge amounts of logs. And also minimizing allocs in key places. These are both general optimizations that paid off on the TUI side too.
Did you experiment with TinyGo’s WASM backend at all? For large codebases it’s easy to run into its limitations but in cases where it can be made to work the resulting wasm can be much smaller, especially if you tweak things a bit.
It sounds like go-app itself would be a problem and considering the size and nature of the project my guess is that it probably wouldn’t work anyway but I am curious if you’ve tried and how far it got.
Yeah, I gave TinyGo a try but I kept running into missing packages/APIs and it was too troublesome to chase down all the broken dependencies (like gRPC). In the end I just threw Brotli compression at the problem and called it a day.
Still think the project is really cool though and would love to try it again in the future.
In addition to the heavyweight runtime and GC, Go’s syscall/js package seems to have been abandoned in perpetual experimental status as well (similar to their WebSockets package).
Go doesn’t seem like the best choice for WASM.
Unless I missed it, there doesn’t seem to be any mention of how it’s actually rendering things in the browser. With React/JSX removed, how are they bridging the cap between WASM and the visuals?
It’s plain old DOM, just driven by Go instead of JS. Go-App gives us a nice React-style component model and does most of the heavy lifting.
If/when we need to, we call into JS directly:
For anyone skimming, yeah that’s ugly, but it’s rare - see the Go-App link for the 99% case.:)
Heya, I’m around and happy to answer questions. :)
I’m curious about how they abstracted over both the web and the tui? Does go-app have a TUI backend? Or is go-app part of the web implementation and the abstraction layer sits above it? Also, I’m very curious how you build an application that works both on the web and TUI without compromising usability on one or both platforms? I would think that the two platforms are sufficiently different from a usability perspective that a single implementation makes for a poor experience on one or both platforms? This seems really interesting–would love to hear more about it!
The TUI and the web UI are both sharing this code:
The
daguipackage interprets our OpenTelemetry event stream and maintains a high level representation of a UI. The TUI code renders it to the user’s terminal using BubbleTea, and our web UI code (private - sorry) renders it with Go-App.So, the abstraction is one layer down, not at the UI widget layer. Like you said, the terminal and browser have very different constraints and strengths - it wouldn’t really feel great to use what feels like a TUI in the browser.
I’m all for single source of truth and sharing code as much as possible, so it sounds like a great move! Some questions that I have:
ariatags for example but don’t have it internalized and probably need to do an a11y audit in general. Thanks for the reminder.)go-app.dev. I will say that when the app crashes, it crashes hard.Here’s v2 and v3 side by side if you want to kick the tires: