In this episode, Surma talks about web apps that (partly) abandon the DOM and use canvas instead, to take rendering matters into their own hands. Figma is one popular app that uses this approach, while Flutter is an entire app platform that went with this technique to provide portability. Jake and Surma discuss the tradeoffs of building apps this way.
Surma:Oh, I thought that would be, like, you know, people in bars with cocaine or something.
Jake:No! No, I wasn't...
Jake:I wasn't on travels as usual.
Jake:No, this was just at home,
Jake:and we were like,
Jake:what if we had a bottle of wine
Surma:Was it a green curry?
Jake:and then another bottle of wine and a curry,
Jake:and see what happens the next day?
Jake:And, uh...
Jake:Curry was good, wine was good, morning was bad.
Jake:It was not...
Jake:It was an Indian curry. It was a
Jake:chicken ra-ra,
Jake:which is a thing.
Surma:A bit of a rah-rah.
Jake:It's a delicious thing.
Jake:A ra-ra.
Surma:In general, I feel incredibly awkward when Thai or Indian, and I love that they put the original names on the menu,
Jake:That's why there's a benefit of
Jake:ordering online. I didn't have to say that out loud
Jake:to a person. Hello, can I get
Jake:a chicken ra-ra?
Surma:but then I feel like my entire order is just me butchering another language with my ignorance.
Jake:Mmm, yes.
Jake:Yeah.
Jake:I tried to learn Italian once,
Jake:and then, for a while,
Surma:Oh yeah, please, can I have the Cacio e Pepe?
Jake:I was that guy in the restaurant
Jake:pronouncing things with an Italian
Jake:accent, until I
Jake:realized that I hate that guy.
Jake:And then I stopped.
Jake:Yeah, exactly.
Jake:Woohoo!
Jake:I was doing the Mario noises as well,
Jake:because obviously he's the world's most famous Italian.
Surma:And the most authentic one as well.
Jake:Exactly.
Jake:I've got a worry
Surma:Oh boy.
Jake:that I want to get off my chest.
Jake:So, my other
Jake:half snores. She's a snorer.
Surma:Are you Snorra?
Jake:I don't...
Surma:I mean, yeah, you said in a previous episode that the boiler kept you up, so yes, that sounds like you're a light sleeper.
Jake:Well, that's
Jake:the question, you see. Because I hate
Jake:the fact that
Jake:my other half snores. Because I feel
Jake:like I'm a light sleeper, or certainly light
Jake:to get to sleep.
Jake:And so the whole snoring thing...
Jake:Yeah.
Surma:First Italian, now snoring.
Jake:Exactly.
Jake:Now, recently,
Jake:my other half has been telling me
Jake:that I have been snoring.
Jake:And it's...
Jake:I'm becoming what I hate.
Jake:You know? It's
Jake:stressing me out. I don't...
Jake:Yeah, exactly.
Jake:And it's
Jake:been worrying me, because I don't want
Jake:to be keeping her awake.
Jake:And obviously
Jake:it feels like a thing that happens
Jake:when you get older as well, so it's kind of reminding
Surma:Yeah, what's next? Are you going to start being a Nigel Farage fanboy?
Jake:me that I'm getting older.
Jake:But, here's a thing that happened.
Jake:I got up in the middle of the night
Jake:for the toilet, because...
Jake:Another reminder that I'm getting old.
Jake:Yeah, exactly.
Jake:Yeah, that's next on the list.
Jake:We'll see how long that takes. But...
Jake:Went to the toilet.
Jake:Got back into bed.
Jake:And my other half
Jake:rolled over,
Jake:went...
Surma:Is that a common occurrence in your relationship?
Jake:She made this really angry noise.
Jake:And then punched me.
Surma:I have never heard myself snore, or know of anyone who has perceived themselves snoring.
Jake:Well, here's the thing.
Jake:She snored,
Jake:got annoyed about it,
Jake:and punched me.
Jake:So now I'm wondering,
Jake:am I actually a snorer?
Jake:Or is it...
Jake:Is she just dreaming about me snoring?
Jake:...
Jake:...
Jake:...
Jake:...
Surma:That's hilarious.
Jake:Oh, no, I have. Oh, absolutely.
Jake:Yeah, I... And so this is the thing.
Jake:I do know that I occasionally snore
Jake:because I wake myself up snoring.
Jake:Yeah.
Surma:Yeah, you stop yourself, so where's the punching coming from? Clearly you would interrupt yourself.
Jake:Mmm. At least I'm waking
Jake:myself up.
Jake:Oh.
Surma:Full Fight Club mode.
Jake:Yeah.
Jake:Yeah, like, I thought she was punching me
Jake:through the night, but then she was away for a week
Jake:and I woke up with a black eye, so, you know,
Jake:just...
Jake:beating myself up through the night.
Jake:Oh. Yeah.
Jake:...
Jake:...
Jake:...
Jake:...
Jake:Hello!
Jake:And welcome to another episode of
Jake:Off the Main Thread.
Jake:I'm Jake.
Surma:Wait, is that how we start? Do we do intros?
Jake:I don't know, man.
Jake:This is how we're starting this one.
Surma:Are you the guy who designed Service Worker?
Jake:I am, actually.
Jake:Oh, do you know what?
Jake:There's been some interesting
Jake:stuff around Service Worker.
Jake:They've added static
Surma:Wait, that has happened?
Jake:routes to...
Jake:Yeah, there's landing ads in the latest
Jake:Chrome. Yeah.
Surma:Sure, go.
Jake:We're now doing a topic in the middle of the intro,
Jake:which I don't think...
Jake:Like, who are you?
Surma:I'm Surma.
Jake:Hi, Surma.
Jake:That's good.
Jake:As usual, or when we remember to,
Jake:I want to thank Shopify for
Jake:paying for all of this
Jake:and the hosting and letting us do it.
Jake:They don't ask us to say that, but we do.
Jake:We do like Shopify. We like Shopify enough
Jake:to both work there.
Surma:Yeah, just about enough.
Jake:Just about enough.
Surma:So straight off the main thread, tell me about these new static routes.
Jake:Yeah, well, look.
Jake:I wasn't planning to talk about this,
Jake:so it's really
Jake:thin on the ground in terms of what's in my head.
Jake:The idea is, it's in the latest
Jake:Chrome, which is Chrome,
Jake:whatever the latest number is.
Jake:Do you know what? Let's do some
Jake:Googling.
Surma:Oh, I thought we were going to say, so in Chrome release, insert Chrome release, and then we can do a nice AI voice insert.
Jake:Oh, look. If we're going to go off the main thread,
Jake:there is this...
Jake:Someone posted it. You know how articles
Jake:sometimes have a button
Jake:on to read the article
Surma:Yeah.
Jake:out loud? Well,
Jake:there is a...
Jake:Someone posted one of these on X,
Jake:formerly known as
Jake:Twitter.
Jake:The article
Jake:has generated this. This is not another tool doing this,
Jake:but the article contains
Jake:the odds of something are one in
Jake:1, 0, 0, 0, 0, 0, 0, 0.
Jake:And then, like, a lot of 0s.
Jake:A lot of 0s.
Surma:Alright.
Jake:And just nothing prepares you
Jake:for how that is read out.
Jake:Because the voice
Jake:is pretty good, pretty human-like.
Jake:And it's just, you know...
Jake:And the chances of that are one
Jake:in...
Jake:It just...
Jake:Look, I'll post a link to that, because that is...
Surma:Yes.
Jake:I'm not doing that justice,
Jake:but it's making me laugh just thinking about it.
Surma:I was really impressed with OpenAI's text-to-speech, which did English pretty well.
Jake:Hmm...
Surma:You just give it German, it just recognizes it's German, and it sounds pretty damn good.
Surma:I really haven't used it that much at all, I just played around with it for like 10 minutes.
Surma:But we've been using OpenAI for our transcriptions on the podcast, and they go off the rails sometimes.
Jake:Hmm...
Jake:It's a little hit and miss!
Jake:Hmm...
Surma:I'm kind of curious if the same would happen with text-to-speech. I kind of doubt it, because it's probably a lot more deterministic in that sense.
Jake:Yeah.
Surma:But yeah, if you look at our transcripts, I've tried to make sure they're all good, but apparently I miss it sometimes.
Jake:Hmm...
Surma:And then you get art, as Monika called it.
Jake:One of us is having a breakdown,
Jake:where it's just...
Jake:One of us going, yes, yes, yes, yes,
Jake:yes, yes!
Jake:Which sounds great!
Jake:It just looks like you're interrupting me
Jake:every other word to say yes, which I like
Jake:the enthusiasm. You should
Surma:Yeah, I'm just, I like your topic. It's called active listening, okay?
Jake:actually do it.
Jake:Right. Let's get off
Jake:this thread, back onto the
Surma:Pop it off the stack.
Jake:off thread. Pop that off
Jake:the stack. Chrome 1, 2,
Jake:3. That's why I did read
Jake:1, 2, 3 earlier, but I was like, well, I can't be 1, 2, 3,
Jake:because that's just numbers
Jake:in order, isn't it?
Jake:It's also a legitimate Chrome version.
Jake:Yes, the ServiceWorker
Jake:static routing API.
Jake:This is where, in your install event,
Jake:you say, hey,
Jake:for requests that look like this, do this.
Jake:And this is
Jake:an
Jake:API sketch that we made
Jake:in
Jake:the very early days of
Jake:ServiceWorker, before it landed in any browser.
Jake:We were like, we probably need this at some point.
Jake:And finally, it's actually
Jake:being done. The API shape
Surma:And the motivation here is it allows the browser to skip creating the service worker JavaScript scope,
Jake:isn't exactly the same, but it's so you can say,
Jake:yeah, requests like this, just get it
Jake:from the cache, or
Jake:requests like this, just go to the network.
Surma:as the service worker is sleeping, and just have that as a static loop.
Surma:It's faster, right? Especially when you open a page after a long time.
Surma:The service worker isn't alive, everything is like, the cache is on disk, not on memory and everything.
Surma:So this just makes, I guess it makes a significant speed-up possible.
Jake:Yes, it is.
Jake:It's a significant speed-up.
Jake:We did see a lot of
Jake:companies try and use ServiceWorker as a performance
Jake:primitive, but it
Jake:never worked out, because
Jake:it has to create a
Jake:worker. It has to run
Jake:JavaScript.
Jake:And, yeah.
Jake:We knew it was not going to work, because this is why
Jake:we sketched this API out in the early
Jake:days. We couldn't get
Jake:implementers to agree to do it, but
Surma:Is it on you to do progressive enhancement, as in like to re-implement the same roots in your service worker imperative code?
Jake:that is happening now.
Jake:So, yes, hopefully you'll see
Jake:ServiceWorker as being faster
Jake:in general, but it certainly
Jake:brings the capabilities of the being.
Jake:Yes, it would be.
Surma:I was going to say, it seems like a little mini-library, you import into your service worker and you just load that.
Jake:For the time being, until all
Jake:browsers support this routing API,
Jake:you would have
Jake:to, you know, it seems
Jake:like the kind of thing that tooling could do
Jake:a pretty good job of.
Surma:Okay, where do you declare those static roots? In your manifest? In a separate file? How does it work?
Jake:No, it's
Surma:Ah, in the events, okay.
Jake:all in the install event.
Jake:So, you are still using
Jake:JavaScript. It's one of those things where
Jake:the term declarative is
Jake:such a, there's a lot of gray area in the term,
Jake:because it's declarative,
Jake:because you're saying it up front,
Jake:but you are saying it up front
Jake:with JavaScript. So you could,
Jake:if you wanted, create your own system where you
Jake:fetch a JSON
Jake:file, and then turn that into
Jake:these calls for adding routes,
Jake:and also, like, you know, then
Jake:put that data in
Surma:I mean, the good thing is that if it's already basically in imperative code in your install event,
Jake:index.db so you can then do the same thing
Jake:in your fetch event, or whatever.
Jake:Yeah.
Surma:that means that dictionary, I guess, of root to cache entry is already in your JavaScript code.
Jake:Yeah.
Surma:So then reusing it in your actual fetch handler is almost trivial.
Jake:Exactly.
Surma:Like you already have to have it in that code scope anyway, but the browser can do the smarts if available.
Surma:So that's, I think it's actually in this case, quite the right choice for the API design.
Jake:Yeah, so
Jake:the way you match URLs is using URL
Jake:patterns, and
Jake:URL patterns is still
Jake:like a Chromium only thing, so
Surma:Is it?
Jake:whatever. Yeah, but there are
Jake:polyfills for it, so yeah. Anyway.
Surma:I mean, yeah, the polyfill is small. I know that Safari doesn't have it.
Surma:I thought Firefox did, but, well, who knows if they still have engineering resources in Safari to actually implement this.
Jake:No, unfortunately not.
Jake:Oh, yeah.
Jake:We should.
Surma:Let's give it a month and see what happens.
Jake:Look, I did my
Jake:shitting on Safari episode
Jake:last time,
Jake:so I'm going to give it a
Jake:break this time.
Surma:And then we do part two, the EU strikes back or the Department of Justice strikes back.
Jake:Yeah.
Surma:Whoever strikes back, that's the one who strikes back.
Surma:Alright, so should we, I mean, I was going to say, should we talk about the web, but you kind of talked about the web.
Jake:Exactly. All right.
Surma:But should we talk about the main thread web?
Jake:This is the thing, like, we
Jake:started, well, just before we started recording,
Jake:I was like, well, you know, I took over the last
Jake:episode completely, so it's your turn,
Jake:and here we are, like, 15 minutes in,
Jake:and it's just been me talking.
Surma:Yeah.
Surma:Yeah, you're just an attention whore.
Jake:So, come on, mate.
Jake:Yeah, yeah, just, like,
Jake:let's, just give me a break
Jake:some. I want to
Surma:Yeah.
Jake:listen to something. So, your
Surma:Then shut up.
Jake:turn. All right. Okay.
Surma:And I thought.
Surma:I was going to talk a bit about.
Surma:I was trying to find a good title, but I think I'm going to go with canvas based web apps.
Jake:Yeah.
Surma:Right, because this kind of got started with because I was talking about Bevy and I'm playing around with Bevy still.
Jake:Yeah.
Surma:And the game engine for us and it has an ECS pattern and blah, blah, blah.
Surma:We talked about all that.
Surma:But one thing that's interesting is that it runs on, you know, Mac OS, Windows Linux.
Surma:But it also runs on the web because it compiles to WebAssembly and hooks into WebGPU.
Surma:And so basically it just creates a canvas, gets the WebGPU context, hooks into the browsers, input events like keyboard, mouse, pointer, gamepad, whatnot.
Jake:Hmm.
Surma:Pipes that all into your wasmified rust.
Jake:Hmm.
Surma:The game engine runs as per usual and then issues the WebGPU commands to draw to the canvas.
Surma:And, you know, that's cool.
Surma:And I think for games that makes sense because the games don't use the DOM.
Surma:The DOM is not in the vast majority of cases, not the right abstraction to do games.
Surma:You want to do pixels and triangles and shaders.
Surma:And so they're usually canvas bound.
Jake:You can do Flappy Bird
Jake:with divs, for sure, but it's
Jake:not the fastest way to do it.
Surma:Yeah, and even there is the question, like, should you?
Surma:Is it actually the ergonomic way or the flexible way if you want to, like, because games also, you know, they optimize heavy for iteration.
Jake:Hmm.
Surma:You do play testing and have to do, like, little tweaks and visual adjustments.
Surma:And I think sometimes the assumptions of the DOM come from document land, right?
Surma:Like they're made for documents.
Surma:And I think for games that is often just not a very good fit.
Surma:So I think in this case, going, you know, canvas for games makes sense.
Jake:Hmm.
Surma:I think in this case, even going via WebAssembly makes sense because you're in canvas land anyway.
Surma:So even if you wrote in JavaScript, there is a lot of things you would have to, you know, write and reinvent because you're really just like controlling pixels.
Surma:And so WebAssembly can offer you benefits there.
Jake:The only
Jake:counter I would give to that is that,
Jake:you know, we had decades
Jake:of Flash games,
Jake:and Flash is
Jake:a scene graph, right, like, which
Jake:is, the closest thing we have
Surma:Yeah, I mean, the flash parallel is interesting because I remember, you know, many of the flash games that I played.
Jake:to that on the web is SVG.
Jake:There were pixel-based
Jake:things in Flash,
Jake:and I guess some games were using them, but a lot
Jake:of games were just moving, you know,
Jake:sprites around in a way you would, like,
Jake:you know, doing animations in
Jake:SVG, but, yeah, I mean,
Jake:that was also in the desktop era, so
Jake:yeah, how that would perform on mobile,
Jake:I don't know.
Jake:Yeah.
Surma:And I tried to look a little bit at flash.
Surma:I mean, I never really got, never understood it at the time because I just didn't have the knowledge.
Surma:But, you know, the first thing you would build is a loading bar because these flash games were sizable.
Jake:Yeah, well, yeah.
Surma:And this is the same here.
Surma:Like the toy game I'm working on, the WASM binary is 35 megabytes.
Surma:And WASM compresses well.
Surma:It broadly is down to six megabytes.
Surma:But that's still pretty heavy for people that are, you know, bundle size obsessed like the two of us.
Surma:That's a lot.
Jake:Yeah, oh, for sure.
Surma:I guess like, you know, many websites spend six megabytes just on a GIF, but it's a lot.
Surma:To be fair, like it's unoptimized, you know, like ECS as a pattern doesn't really lend itself to tree shaking.
Surma:You load plugins and then these functions run systems and they can't really tell whether there is any entities in your game that use the components,
Surma:these systems processing, so that's something where I could actually go in and probably rip out a lot of stuff and optimize a bit.
Surma:Anyway, so this is, again, for games.
Jake:Hmm.
Surma:And I think games also get a bit of a, like a free pass.
Surma:They're allowed to be a bit bigger.
Surma:I mean, look at what AAA games are consuming on consoles.
Surma:Not that you shouldn't care, but, you know, I guess downloading six megabytes for a good game is not outrageous, especially if you cache it well.
Jake:Oh, I love that thing
Jake:where I, you know,
Jake:I don't tend to do it anymore because of this,
Jake:but when you actually buy a game on
Surma:Yep. Yep.
Jake:CD, and you're like, well, this is
Jake:good because one of the advantages is all
Jake:the data is right in my hand,
Jake:so I'll just put the disk in
Jake:and install it, and then afterwards
Jake:it's like, I'm just going to download a little
Jake:tiny update of
Jake:30 gigabytes. Yeah.
Surma:That's great.
Jake:Yeah, okay.
Surma:Now, with games, sure, but it is also kind of tempting to start thinking about Bevy and similar things for apps.
Surma:Like for not like interactive 60 frames a second shooter thingies, but like apps with buttons and layouts.
Jake:Hmm.
Surma:And after all, like Bevy uses CSS for layout.
Surma:So like this, you know, flex and grid and all these things.
Surma:And it's kind of like, oh, I could just build my app with this and you gain control at a level that in the browser you sometimes don't.
Surma:I think.
Surma:And, you know, for me, like my immediate intuition is that for an app, the benefit cost ratio is off.
Jake:Hmm.
Surma:But it's worth looking at because there is, you know, a couple of apps out there that kind of take this model.
Jake:Hmm.
Surma:This model can be successful.
Surma:Like I guess most notably Figma works in this model where the big design area they have in the middle.
Jake:Yes.
Surma:That's a canvas and it's driven by WASM.
Surma:I think it's interesting to point out that the UI around this big canvas where, you know, you see your elements and the different attributes that is done in DOM.
Surma:So they kind of do a mix and match approach.
Jake:Oh, interesting.
Surma:And I think that is because if you did it all in WASM, you have to reinvent a lot of wheels.
Jake:Hmm.
Surma:Yeah, I think they said like the text rendering for big documents just wasn't working the way they needed to and couldn't control it enough.
Jake:Oh, for sure. Like, lots
Jake:of mouse interaction stuff, which I guess they are
Jake:somewhat doing as well.
Jake:I'm actually just clicking around Google Doc
Jake:here because I feel like
Jake:Google Docs
Jake:is Canvas. It looks, again, it's a
Jake:mix. It seems like big areas
Jake:of text are just Canvas
Jake:element.
Jake:Yeah.
Surma:So I guess the text area might be different, but I guess the UI outside is still DOM.
Surma:I don't know.
Surma:I vaguely remember something like that.
Jake:It's weirdly
Surma:But yeah, I.
Jake:chunked little bits of Canvas. Sorry,
Jake:I'm now, like, yes, all
Jake:your toolbar buttons are DOM,
Jake:whereas the main text is
Jake:a kind of, yeah, chunks of Canvas.
Jake:It's quite, yeah, all right. Well, that was fun.
Jake:Sorry.
Surma:Interesting, I guess.
Surma:I guess you would want to like cache them, the pages, because, you know, most of them will be static.
Jake:Hmm.
Surma:Like, you know, yeah, I don't know.
Surma:I don't know.
Surma:I guess that would be an interesting deep dive to do at some point.
Surma:But yeah, like I was playing out with Bevy and kind of realizing I get a lot of very interesting control and I can avoid things that are slow in CSS because I have very granular control over like which algorithms to use, which cost to pay.
Surma:But it also means you lose access to a lot of things that the browser has already done for you.
Jake:Hmm.
Surma:Like, for example, you know, Bevy implements a scene graph with the whole concept of you can have children elements on a parent element.
Surma:And if you move the parent element, all the children move as well.
Jake:Hmm.
Surma:The browser already has this logic in the DOM and CSS.
Surma:It even has it for 3D.
Surma:You can literally rotate a DOM element in 3D space and the children will rotate the same way in 3D space.
Surma:It's already there.
Jake:Yes.
Surma:But if you go canvas, you have to reimplement all of that where you propagate a change to a parent down to the children and vice versa.
Jake:Hmm.
Surma:You know, if the user clicks the mouse, you have to do hit testing.
Surma:You have to figure out which element is currently under my mouse cursor.
Surma:The browser has that logic built in where, you know, the browser just gets here are the mouse coordinates on screen and the browser figures out, OK, that means this DOM element is the one that has been clicked.
Jake:Yes. And there's lots of
Surma:...
Jake:subtlety there. Like, you know, was this
Jake:a tap on an interactive
Jake:element, or was this a
Jake:drag to scroll?
Jake:And, yeah, that is all
Jake:just built into the browser for you.
Surma:Yeah.
Surma:...
Jake:But if you're handling scrolling yourself,
Jake:if you're handling interactivity yourself,
Jake:that is code you are going to write again
Surma:Exactly.
Surma:...
Jake:and probably get it slightly different
Jake:to the platform because it's probably
Jake:different between iOS and
Jake:Windows and Mac and all of that.
Surma:And that's one of the parts where I really realized that this rabbit hole is really deep because I was building a menu, you know, the opening menu of a game where you have play, settings, options and quit or something.
Jake:Hmm.
Jake:Hmm.
Surma:And I realized, you know, I have to catch intercept the arrow, like I have to listen to the arrow keys and then keep the state where the focus currently is and figure out what is the next element.
Surma:And I realized I'm kind of like reinventing spatial navigation.
Jake:Yes.
Surma:And I was saying, you know, if I were to, yeah, if I were to make like a drop down, you know, in browsers, when you have a drop down, you can start typing and it jumps to the value that starts with the same letters.
Jake:Spatnav.
Jake:Hmm.
Surma:...
Jake:It's very different between platforms
Surma:Exactly.
Jake:as well, but yes, yes.
Surma:And so I was thinking, you know, that is something that people in their browsers are probably used to very specific behavior.
Jake:Yeah.
Surma:And now you are building probably new behavior doesn't match any of the browsers or at best one of the browsers.
Surma:...
Surma:So that's also something where you actually provide like this uncanny valley experience to your users if you go down this route.
Jake:Hmm.
Surma:...
Jake:Hmm. Yeah.
Jake:This is the tricky part with, like, customizable selects
Jake:as well is, like,
Jake:yeah, right now browsers are free
Jake:to show a select element
Jake:however they want, and you see that with
Jake:the difference between mobile and
Surma:Yeah.
Jake:desktop.
Surma:...
Jake:Whereas, yeah, to create a customizable system,
Jake:it's going to have to be somewhat the same.
Surma:I guess the point I make is this is all code that is going to be shipped, like has to go down the wire in your WASM to run.
Jake:Hmm.
Surma:And that's, I think, where these 35 megabytes eventually come from, because there's all this code that is technically already in the browser.
Jake:Hmm.
Jake:Hmm.
Surma:But you decide that you want full control and there's no way for you to reuse what the browser already has.
Jake:Hmm.
Jake:Hmm.
Surma:You have to start from pretty much scratch.
Jake:Hmm.
Surma:And yet there is, I feel like there is a bit of like a trend, not a trend, but there are some people exploring this more and more.
Jake:Hmm.
Jake:Hmm.
Jake:Hmm.
Surma:Especially with WebAssembly becoming more capable, more integrated or integratable with browser APIs.
Jake:Hmm.
Jake:Hmm.
Surma:And that you can just, you know, you can just use WebGPU from within WASM.
Jake:Hmm.
Surma:I say just, there's a bit of magic happening, but it's, you can do it.
Surma:And it's not slow doing these back and forth calls between WASM land and browser land.
Jake:Hmm.
Surma:So it's becoming more viable.
Surma:And one platform, I guess, that is trying to see this through to the end is Flutter, right?
Jake:Hmm.
Jake:Hmm.
Surma:Like you, that's the whole, you write your code.
Surma:It's kind of right once run everywhere again.
Jake:Hmm.
Surma:But you write your app, you write in Dart, and then you can build it to Android, iOS, web, and even desktop native kind of thing.
Jake:This is where I'm getting
Jake:excited about this topic because I remember
Jake:looking at Flutter a while ago
Jake:and not
Surma:Yeah, so I should, you know, ask, like I did look at Flutter in preparation for this.
Jake:liking what I saw, and I know you've
Jake:well, because of
Jake:how they reinvent
Jake:the world, and I'm
Jake:interested to know if they've changed
Jake:their practices.
Surma:And, you know, I look at some demos and I want to talk about those, but obviously I am not an expert.
Surma:I'm not involved in the Flutter ecosystem at all.
Surma:But I will say I was kind of impressed.
Surma:There's also things I don't like, but like in terms of, there are clearly things that are not perfect.
Jake:Hmm.
Surma:There are things that are uncanny valley, but they have done a thorough job as far as they could take it.
Jake:Hmm.
Jake:Oh, okay.
Surma:So, and the thing, what I always liked about Flutter or what I was always jealous of is that they just went with, you know what?
Jake:Hmm.
Surma:We are going to animate layout.
Surma:Like the cardinal sin of web development, if you animate any CSS properties or layout like margin or position or anything like that,
Jake:Hmm.
Surma:like Paul Lewis will appear and slap you on the wrist because you are doing things slow.
Jake:Hehehe.
Surma:It's not on the compositor.
Surma:And in Flutter, they're like, oh, we kind of just designed our layout system to handle doing layout on every frame.
Jake:Okay.
Surma:They can do it at 120 frames per second, which is kind of amazing when you think about,
Surma:and this is something the demo that I found online shows, they have similar to media queries breakpoints where, you know,
Surma:like on mobile, you have multiple sections underneath each other, but on a wider screen, they kind of shift next to each other.
Jake:Hmm.
Surma:If you resize your window, it doesn't just snap.
Surma:These boxes are animated into their new position.
Surma:And that's slick.
Surma:It looks really cool.
Jake:Nice.
Surma:And so, yeah, it does work and you get that across the platforms, like no matter whether you're Android, iOS or web,
Surma:like that will work because they are taking full control at the lowest level.
Surma:One thing that I found was really like ironic as I was looking at the showcase section on the website,
Surma:which has like a little animation of logos of the companies that are using Flutter.
Surma:And it's a 1200 by 1600 GIF that weighs six megabytes and looks really jank because it's 30 frames per second.
Jake:Hehehe.
Surma:And I was like, if you have the system that can compile to web, why are you doing this?
Jake:Amazing.
Jake:Yeah.
Jake:Oh, and I'm sure
Jake:even then, any video format
Jake:would have been better than that GIF, right?
Surma:Probably, yeah, this should be, I don't know, not AVIF, but an AV1 or why not a Flutter app?
Jake:Yeah.
Surma:It could be, it would probably be smaller, definitely smaller than six megabytes.
Jake:Yeah, absolutely.
Surma:Anyway, so like I looked at, they have a couple of demos.
Jake:Yeah.
Surma:I looked at the showcase demo for Material 3 and I'll link to this in our description.
Jake:Hmm.
Surma:So that one is not WebAssembly.
Surma:That's, they compile your Dart to JavaScript and add all their runtime stuff and then use CanvasKit to do the rendering.
Surma:CanvasKit is kind of surprised me here because CanvasKit is Canvas compiled to WebAssembly.
Surma:So like Skia, the engine that Chrome uses to render Canvas, if you compile that to WebAssembly, that's called CanvasKit.
Jake:Hmm.
Jake:Hmm.
Surma:And I'm sure they have their reasons, but it's kind of weird to me that they're loading CanvasKit when you have the Canvas in the browser.
Surma:I'm assuming that CanvasKit offers you more control than what the browser Canvas currently offers.
Jake:Or it's a
Jake:cross-platform thing, maybe.
Surma:Oh yeah, maybe it's that, but you know, like it's basically what it adds up to this.
Jake:You know.
Surma:This demo adds up to 2.5 megabytes.
Surma:So it's 1.5 for the WebAssembly CanvasKit and one megabyte.
Jake:Hmm.
Surma:So CanvasKit is Wasm, but your code doesn't compile to Wasm, your Dart code from your app.
Jake:Hmm.
Surma:So yeah, and the CanvasKit WebAssembly is 1.5 megabytes and your Dart compiled to JavaScript ends up being a megabyte.
Surma:There is in their beta channel support for WebAssembly.
Surma:It is currently, as far as I understand, Chrome only because they rely on Wasm GC.
Jake:Oh, yeah.
Surma:So it allows them to not having to re-implement a garbage collector.
Surma:I don't know if it performs faster or slower.
Jake:Hmm.
Surma:I didn't benchmark it, but it is smaller a little bit.
Surma:Rather than 2.5 megabytes, it's 2 megabytes where your app code is now 700 kilobytes rather than a megabyte.
Jake:Hmm.
Surma:And they use a new fork, I guess, of CanvasKit called SKWasm, which is quote unquote only 1.1 megabyte.
Jake:Hmm.
Surma:I'm assuming it's a bit more tailored to the use case and they're probably removing some stuff that they don't need.
Jake:Hmm.
Surma:Not quite sure, but that's just for fairness that they are working on a WebAssembly version.
Jake:Okay.
Surma:So I guess with these sizes, the first load is definitely just going to be a bit meh compared to a handcrafted DOM-based solution.
Surma:But to their credit, even the default template, when you get started, immediately ships with a service worker
Surma:and caches all these static runtime thingies so that the second load is pretty snappy.
Jake:Okay.
Surma:Gotta give it to them.
Surma:Even though they are definitely transferring lots of data, they're doing their best to make that work.
Jake:Okay.
Surma:And so, yeah, I mean, what do you get for that kind of file size?
Surma:You get that animated layout that works.
Surma:You resize the window and the things move around.
Surma:It's really cool.
Surma:Tab works.
Surma:They rebuilt that.
Jake:Right.
Surma:Not only that you can focus through with tab, but they even have a bunch of custom elements to integrate with the browser's ARIA roles and screen readability.
Surma:It seems correct.
Jake:No.
Surma:It seems all right.
Surma:I worry that it's not matching the browser's native tabbing behavior across browsers because I don't think tabbing behavior is standardized.
Jake:No.
Surma:I'm not a screen reader user, so I don't know what that feels like for a screen reader user who might be used to a specific tabbing behavior on a normal web page.
Surma:And now this one may be feeling navigating it differently.
Surma:I don't know if that will make screen reader users get lost or not.
Jake:It's just so much code
Surma:That's just a worry.
Jake:to do what's already there, so
Jake:it's making me sad still.
Surma:It is, for sure.
Surma:And that's the other side.
Surma:What do you lose?
Surma:One of the first things I noticed.
Surma:You can't select text.
Surma:There's all this.
Jake:Oh, right!
Surma:I think it's something you can build or maybe you can have a...
Surma:I'm sure that's been solved where you can probably say this text should be selectable.
Surma:But if you have a sidebar with menu entries, you can't select those texts.
Surma:As far as I could tell, in input fields, you don't get autocomplete.
Jake:Hmm.
Surma:Extensions will not work if they usually have something, I don't know, sensors backwards or you have some custom color styling to, I don't know, maybe to address a site inhibition that you have.
Jake:Hmm.
Jake:Hmm.
Surma:Or ARC's new zapping feature.
Surma:Or not new, but ARC's zapping feature where you can remove certain DOM elements or restyle them.
Jake:Hmm.
Surma:All of this won't obviously work.
Surma:And that makes me think, I'm guessing, does SEO even work with Flutter?
Jake:Hmm.
Surma:Because there's nothing in text in the HTML.
Surma:There is some text in the ARIA tree, but I'm not sure that's respected or consumed by crawlers.
Surma:Anyway, so this is not like me wanting to go Flutter is good or Flutter is bad.
Jake:Hmm.
Jake:Interesting.
Surma:That's just like as a platform that is doing this with, I suspect, lots of funding and engineering power and engineering expertise behind it.
Surma:And this seems like the best they can do.
Surma:That's why I think it's an interesting signal of where we are at in this world.
Jake:Hmm.
Surma:And that kind of reminded me of somewhat abstract proposal by Hixie that he put out a while ago.
Surma:So I guess some people may not know Hixie.
Surma:He's like the original author of the ACID2 and ACID3 tests.
Surma:He was a spec author on HTML and CSS and came up with PingBag.
Surma:And then until recently, yeah.
Jake:HTML5 was
Jake:his, yeah.
Surma:And until recently, he worked on Flutter.
Surma:He left, I think, at the end of last year, but he worked on this.
Jake:Hmm.
Surma:So that puts a bit of context, I guess, where he came up with this.
Jake:Hmm.
Surma:Basically, what he was proposing, it's a public document, so I'll link to it in the description.
Surma:He called it towards a modern web stack.
Surma:I would call it a minimal web stack, but he was basically saying,
Surma:what if we focused the platform standardization efforts towards four major API services?
Jake:Hmm.
Jake:Hmm.
Surma:WebGPU, WebAssembly, ARIA, and HID.
Surma:Which, if you think about Flutter, that makes sense.
Jake:Right.
Surma:So you want rendering.
Surma:You want code execution.
Surma:You want, I guess, ARIA for like, you know, SEO, but also accessibility,
Surma:that kind of like reflection API, so that external systems can inspect
Surma:what is kind of being shown on screen.
Surma:A standardized way to represent what is on screen, semantically speaking.
Jake:Hmm.
Surma:And HID, human interface devices, to give the user inputs.
Surma:I guess output isn't really in here.
Surma:Like, there's no web audio, I just realized, or anything like that.
Surma:I'm guessing that would also be part of it.
Jake:Yeah, you'd want that.
Jake:It is just like taking all of the lowest level
Jake:things we have on the web.
Surma:Yeah.
Surma:Yeah.
Jake:So I guess you'd have
Jake:file system API there at some point.
Jake:But it's kind of like, what is the lowest
Jake:of low level?
Jake:It'd be interesting to know what the proposal there was
Jake:for storage and stuff like that.
Jake:Do you want some kind of
Jake:byte storage system? But maybe
Jake:file system would give you that, I guess.
Jake:I don't know.
Surma:Like, origin file system access would probably be enough,
Jake:Hmm.
Surma:and then extra permission if you want actual file system or something.
Surma:I feel like we have, like, a lot of the puzzle pieces are kind of there.
Surma:I'm guessing the ARIA one is the one that's most lacking to actually integrate.
Surma:And also, in my head, it's like, yes, those are the lowest level primitives,
Surma:and they should be as powerful and flexible as they can be.
Jake:Hmm.
Surma:But what I think that should be, obviously, I think this is completely
Surma:unrealistic at this point, also because of backwards compatibility.
Surma:But I kind of feel like what would be cool is to work towards, like,
Surma:a modular browser.
Surma:Like, again, like, this is never going to happen at this point,
Surma:but it feels kind of cool where, yes, you have these low-level primitives,
Surma:like the ones that Higgs suggested, but on top of that,
Surma:you have a whole battery of kind of, like, standardized modules.
Surma:You have your CSS cascade module, your CSS layout module, your DOM module.
Jake:Hmm.
Surma:Basically, anything that's a web standard would be a module
Surma:that the browser provides and loads by default.
Surma:But you, as a developer, you could opt out of any individual module
Surma:and just, like, not use it or maybe provide your own replacement.
Surma:So if you don't want to pay the cost for multi-pass layout,
Surma:you could provide your own CSS layout module as a WebAssembly module.
Surma:You want to go back to, like, vBasic script tags?
Jake:Oh, VBasic as in Visual Basic.
Surma:Go for it.
Surma:Just replace the JavaScript module with your vBasic module
Surma:that is compiled to WebAssembly.
Surma:Yeah.
Jake:I thought you were sort of
Surma:Is it basic? No, like, the visual.
Jake:regressing into German there.
Jake:VBasic?
Surma:But the script tag.
Surma:Well, it was script language vBasic, wasn't it?
Surma:I never used it.
Jake:Yeah, it was.
Surma:I just remembered that was a thing where, you know, you could do that.
Jake:VBScript.
Jake:Yeah.
Surma:Like, I don't think this is going to happen,
Jake:Oof.
Surma:and I'm not sure it is actually worth putting steam behind this.
Surma:But there were some, you know, developments in this direction.
Surma:Like, Houdini.
Jake:Well, Houdini.
Surma:Yeah, like, at least for CSS, it did try to, like,
Jake:Right, there we go.
Jake:Yeah.
Surma:provide your own paint routine with CSS paint worklet,
Surma:even though I think it was a bit cut off at the knees
Surma:in terms of what it could do.
Surma:But they had plans for, like, augment the parser,
Jake:Yeah.
Surma:provide your own CSS parser extension,
Surma:or they had the layout worklet to provide your own layout
Surma:where you could...
Surma:I guess this would be interesting if it hasn't happened.
Surma:It was never really fully specified.
Jake:Hmm.
Surma:I think it was like a prototype behind the flag.
Surma:But I think, theoretically, it could have allowed you
Surma:to just make layout so cheap or fast
Surma:that you could end up in a world like Flutter
Surma:where you can just run layout in every frame.
Surma:If you don't do floats and don't do intrinsic sizing
Surma:and it's always single pass top-down,
Jake:Hmm.
Surma:that could have worked.
Surma:But sadly, I think Houdini has not really made any progress.
Surma:It's kind of stagnated.
Surma:I think the focus is currently somewheres...
Jake:Yeah, it feels abandoned,
Jake:doesn't it, at this point?
Surma:Yeah, a little bit.
Jake:It's a shame, isn't it? Because every
Jake:runtime CSS polyfill
Jake:sucks.
Jake:It's rubbish.
Surma:Yeah.
Jake:They're good enough for playing
Jake:around with. They are not good enough
Jake:for production because they are slow
Jake:and there's
Jake:booby traps everywhere.
Surma:Yeah.
Jake:It's going to not behave like
Jake:the real thing because just
Jake:CSS is not extensible
Surma:One of the last things I did when I was still at Google
Jake:in that way.
Surma:was writing a polyfill for container queries,
Jake:Hmm.
Surma:which I was quite proud of because it was very small
Jake:Hmm.
Surma:but did most of the things.
Surma:And I put in a readme, like,
Surma:this is very much intentionally a pragmatic approach.
Surma:Like, I want to solve the 80% use case for container queries
Surma:without having to implement a full CSS engine.
Surma:I think it did it all right.
Surma:You know, the kind of thing where it's not going to work
Surma:with the cross-origin at import statements
Surma:or anything like that.
Jake:Right.
Surma:But like I said, it would be mostly for playing around with
Surma:not to actually high-fidelity polyfill the functionality
Surma:because it's nigh impossible with almost all CSS features.
Jake:Yeah.
Jake:Yeah, a little peek behind the
Jake:curtain for folks there is that
Jake:we would make those kind of polyfills
Jake:to get people excited about features, to get people
Jake:playing with them and testing them and saying what they
Surma:Yeah.
Jake:liked and didn't like. The downside
Jake:of it is we ended up with Chrome
Jake:project managers
Jake:going out and saying that this
Jake:feature is now cross-browser because
Jake:the polyfill exists
Surma:Yeah.
Jake:and that, like, we found
Jake:incredibly frustrating and
Jake:we felt it was a lie.
Jake:It was really
Jake:misleading.
Jake:Before I left,
Jake:we kind of wrote up
Jake:what the rules
Surma:Yeah.
Jake:for saying something is cross-browser
Jake:and part of that was to explicitly say
Jake:polyfills don't count
Jake:unless they count
Jake:as gold standard polyfill
Jake:and I wrote this definition which basically
Jake:meant the only thing that would pass
Jake:as gold standard really would be little
Jake:JavaScript methods being added, like
Jake:if they come up with a new array method
Jake:because you can polyfill that 100%.
Surma:Yeah.
Jake:Yeah, so these are the polyfills which are
Surma:Yeah.
Jake:kind of doing it, but it's going to be too slow
Jake:for production and it's going to be messy
Jake:all around the place.
Surma:Would transpilation count?
Surma:Like, you can have now async iterators if you run Babel first.
Jake:So,
Jake:I wanted
Jake:transpilation to count, but I think
Jake:others didn't, so I don't think it does count.
Surma:Hmm.
Jake:But yeah, I think
Jake:transpilation is sort of okay.
Surma:I don't think I have, like, a big hitting summary to end on.
Surma:I just thought I find a really interesting thought experiment
Surma:to think about what if you could take more low-level control
Surma:of how your apps are being consumed by the browser
Surma:without having to reinvent all the wheels
Surma:and ship down that code.
Surma:It definitely, like, in certain specific contexts,
Surma:I think it is the right choice.
Surma:Like, did Figma make the right choice with going Canvas and Wasm?
Jake:I think
Surma:I mean, they're doing the mix-and-match approach,
Surma:but I'm assuming they did it for good reason.
Surma:I feel like they would.
Surma:It is a heavy lift to build the thing that they built.
Jake:if you were going to recreate Figma with
Jake:let's say SVG,
Jake:you're going to have
Jake:to control tabbing
Jake:anyway because
Jake:the order, the layering of
Jake:things, you
Jake:would have to implement spatial navigation of sorts,
Surma:Yeah.
Jake:right, and
Jake:trying to make sure the correct semantics are there
Jake:for the text and things like arrows
Jake:from one thing to another. I think
Surma:Yeah.
Jake:the web doesn't have a ton to offer them there
Jake:for that kind of thing, so
Jake:going for
Jake:WASM canvas rather than SVG,
Jake:it seems totally
Jake:reasonable if they got a performance benefit
Jake:from it. I don't think they're throwing away
Jake:a lot of stuff
Jake:that the browser gives them already, whereas I do
Jake:feel that Flutter is.
Jake:I worry I'm
Jake:being too negative on Flutter
Jake:because another peek behind the curtain,
Jake:what would happen
Jake:when we were at Chrome?
Jake:The Chrome developers'
Surma:Yeah.
Jake:Twitter account,
Jake:we had the keys to that, but so
Jake:did various marketing teams
Jake:and we would
Jake:wake up sometimes to find
Jake:a tweet on
Jake:the Chrome account essentially saying
Jake:you want to build any kind of web app? You should use
Jake:Flutter and nothing else.
Jake:They'd be like, excuse me?
Surma:Yeah.
Surma:Yeah.
Jake:I'd just go in and delete it and then
Jake:there'd be a ton of fuss.
Jake:I'm going to delete that and then America will wake up
Jake:and then we can have a little chat about it.
Jake:I used to get frustrated at that
Surma:Ah.
Jake:especially because
Jake:the other thing that we were trying to
Jake:say is please
Jake:don't re-implement everything the browser already has
Jake:to end up shipping
Jake:two megabytes to display a heading.
Surma:No.
Jake:I maybe should be more open-minded about it.
Jake:I do agree.
Surma:No, like, I think it's, like, for normal apps,
Surma:they literally, in Bevy as well,
Surma:like, you literally ship an entire font rasterization engine.
Surma:Like, that's not trivial.
Surma:Like, on one hand, amazing that Bevy has an implementation of that,
Surma:but they read the font files and, like, rasterize all of that
Surma:with hints and subpixel rendering.
Surma:But at the same time, like, it's in the browser.
Jake:You can, right?
Surma:Wouldn't it be cooler if we could hook into that?
Jake:You can use a regular canvas element
Jake:and write text to it.
Jake:I imagine Devi's not doing it
Surma:Yeah.
Jake:for the same reason as Flutter.
Jake:It's to be cross-platform.
Jake:To be cross-platform, it's easier for them
Jake:to implement everything their way from scratch
Jake:because then it's the same.
Surma:I think for Bevy, it's actually more like it's an open-source,
Surma:unpaid project until recently,
Surma:where it's just, like, passionate people working,
Surma:and that is just the easier thing to do to, you know,
Jake:Oh, yeah, yeah.
Surma:load a rasterization.
Surma:What is it called? Is it called Harfbuzz?
Surma:They're pretty much the de facto standard for font rendering,
Surma:and they're having bindings to that.
Surma:But I was thinking, like, it would be cool if I could literally
Surma:generate the font atlas in JavaScript with Canvas
Surma:and just load the texture into my WASM,
Surma:and basically that would allow me to skip the entire font code
Surma:in my WASM bundle if I were to publish this to the website.
Surma:There's a couple of interesting things to explore at some point.
Jake:Yeah, it's definitely
Jake:the thing aside
Surma:But, again, that's a lot of work, and it's probably not trivial
Jake:where they want the consistency across platforms
Surma:to make this work reliably and unilaterally.
Jake:there is definitely a problem
Jake:with the web right now
Jake:in that you have to either
Jake:go
Jake:use the platform
Jake:or not.
Jake:It's that in-between thing
Jake:that hooking into CSS
Jake:and changing bits like that.
Jake:It's that in-between thing
Jake:that hooking into CSS
Jake:and changing bits like that.
Surma:Yeah.
Jake:Providing your own layout engine,
Jake:even providing your own font rendering,
Surma:No, not really.
Jake:that kind of stuff.
Jake:If you want any of that,
Jake:you have to throw the whole lot away
Jake:and start from scratch.
Jake:We talk about the extensible web a lot
Jake:on this show,
Jake:but that was the dream there,
Jake:and it just hasn't been realized.
Surma:But, yeah, that was thoughts,
Surma:random thoughts by Surma on Canvas-based web apps.
Surma:And we still made it to 50 minutes.
Jake:Yeah, it's still a long episode.
Surma:So, you know.
Jake:We keep trying to do them shorter,
Jake:but we're...
Surma:Nah, we're just too self-indulgent.
Jake:Yeah.
Surma:But maybe if we don't waste any time, we can just end the podcast here
Surma:and call it.
Jake:Oh, okay.
Jake:Yeah, and then we'll see each...
Surma:Did you have any parting words? Any wisdom?
Jake:Any final words?
Jake:Wow.
Surma:I did make, like, a chop gesture with my hand,
Jake:Oh, you said parting words.
Jake:Sorry, I thought it was a bit more
Jake:threatening than that.
Jake:No.
Surma:which you obviously can't see, but, like, you know.
Jake:Oh, okay.
Jake:And then, yes, we'll be back
Jake:with whatever cadence
Jake:we manage.
Jake:And talk a bit
Jake:about putting React
Jake:in the browser.
Surma:Oh.
Jake:We've not done throw-forwards before,
Surma:Oh, that's, yeah, I'm excited now.
Jake:but we'll give it a go.
Surma:I'm excited now. That sounds inflammatory.
Jake:And what will inevitably happen
Jake:is something more important we'll come up to talk about,
Jake:and we'll just never talk about the React thing again