WebAssembly, your browser’s sandbox
(mellow music) (audience applauds) - Thanks, John, and thanks, everyone, for having me here. The article I think John was referring to is where I said that Microsoft needed to kill Internet Explorer, and this was several years before they actually announced Edge, and I was actually a Microsoft MVP at that point in time for, ironically, Internet Explorer. So I wrote this article about why they should've killed the product that I was supposed to be supporting in the community.
But I'm not here to talk about Internet Explorer because it doesn't support WebAssembly.
I'm here to talk about WebAssembly in the browsers. So who's writing WebAssembly code like this today? No one? Good, 'cause if you were writing the WebAssembly Text Language, which is what I've got up on screen, I'd like to speak to you afterwards and work out why. But we're gonna be talking about WebAssembly today, and I've been talked about by pretty much every speaker so far, so there's no pressure on me.
So now it's time to learn WebAssembly.
So what we're doing here is we're starting off by bringing in a function from our host of our WebAssembly application called log from an object called console.
It takes two arguments.
They are of int 32 types that are being passed into it, and we'll get that sent to our WebAssembly machine. I'm then going to be requesting one page file of memory in my WebAssembly application, and then I'm going to be putting some data into that. So I'm gonna be popping this onto the stack at position zero and I'm gonna put the value of Hi, H-I, in there, and that's what will be in our memory table. Finally, I'm gonna be exporting a function from WebAssembly called writeHi.
That function will push two values onto the stack. The first one is gonna push a 32-bit integer constant of zero, and then it's gonna push the second one, which is a 32-bit integer constant of two.
Both those will pop onto the stack.
We're then gonna call that log function, which will then pop those both off the stack, and hopefully we will write out a message, or we'll send some values at least correctly through to our log function.
We then have this in JavaScript, and we need to, we use JavaScript to invoke this, and it starts looking like this.
Defining our memory table at the top there, and it's gonna have an initial size of one. We're not gonna grow this memory table, but it's gonna look like that.
We then have a log function to find.
It has two arguments passed into it, an offset and a length.
Now, this is now JavaScript, so it is intrinsically untyped but implied as typed. So we have types in WebAssembly.
We don't really have types in JavaScript.
We're going to be converting that memory buffer into a Uint8Array.
We're gonna be then taking a subsection of that memory buffer based off of the two arguments we've been provided.
That's gonna be decoded as a utf8 string into a value that I can console.log to the browser. I'm then going to create an object that'll pass through to my WebAssembly application when it starts. I will then fetch my WebAssembly binary.
I will then instantiate our application, and hopefully with all of that, I will write out the message to the console of Hi. Thank you for coming to my WebAssembly talk. (audience applauds) So that's a little bit confusing, if I'm to be honest. It's probably not the most readable language. I did do Assembly programming back in university. I learned how to convert Assembly to binary and back from that to Assembly, and it's not something that I wanna spent a lot of time doing.
But I kind of just jumped this talk straight into looking at raw WebAssembly, and that's probably not going to be the most accessible thing to most people.
So let's take a step back and think about, well, what is this thing that I'm talking about and why is it of interest to us? So what is WebAssembly? That's probably where I should've started, not just started with code.
WebAssembly is essentially the evolution of the asm.js specification, or at least loose specification.
So asm.js was defined in around 2013 by Mozilla. It was defined as a strict subset of the JavaScript programming language to allow high-performance applications to be built. Because it was such a strict subset of the language, it meant that the browser manufacturers or the JavaScript engine producers were able to optimise for that and remove a lot of the things that JavaScript has that can make it a bit more difficult for high-performance scenarios.
So they could then target those optimizations and skip things like the jit step so that you didn't have to do that and hopefully run your application a lot faster. One of the earliest examples that they did with this was run the Unreal engine in the browser, in one of the earlier versions of Firefox.
But this wasn't a language that you actually wrote. It was a compilation target using LLVM or C and C++. That was what asm was defined about, and WebAssembly is, as I said, the evolution of that. Instead of using JavaScript as the language, we have now an entirely new stack-based virtual machine that can run withinside of your JavaScript runtime, be it in node.js runtime or a browser.
I'm gonna obviously focus on the browser today, but you can use this with node.js.
Come see me afterwards if you'd like to know more about that end.
So what we get is a compiled binary.
So instead of having JavaScript or a compilation target of JavaScript, we are now gonna be using a compilation target of a compiled binary.
This is still designed as something that we would compile from another higher language like C, C++, Rust, Go, et cetera, and not actually write ourselves.
Like, you don't wanna be writing that stuff that I showed on the first slide.
That's a little bit undesirable.
But because we have this new virtual machine, we're able to control the memory that's available to it. This can help us avoid things like the memory overflow issues that you might get from an application that's running just inside of the same memory space as JavaScript. And also, it means that we can start normalising the underpinnings of our application.
We no longer have to worry about, well, how much is, memory's available on this machine? I'm giving that directly to it.
Or what is the hardware that is available to this runtime? Well, that's been denormalized by the fact that this is running in a virtual machine. It only supports a small set of data types. It supports 32-bit and 64-bit integers and 32-bit and 64-bit floating point values, which means that that's gonna be kinda difficult to build complex applications if you're just running it directly.
That's why it is still seen as a compilation target of higher level languages. C and C++ were what it was originally designed around, but many other languages have started targeting this. Go, Rust, .NET, and a whole bunch of other things. Now, I admit that I didn't actually get to spend a lot of time building cgi.bin applications back in the day, but I don't think I've ever sat there and gone, you know what I need for my web application? It's more C++.
Particularly running in my browser.
Yeah, I don't think that's really what it is. So what does WebAssembly mean for us as front end web devs, or as people building JavaScript applications? And we touched on it a bit in the keynote that WebAssembly, it's a very exciting emerging technology, but don't see it as a complete replacement of JavaScript. Now, I know that some approaches that people are making with WebAssembly are seeing it as a full replacement for JavaScript, and my personal belief is that that is the wrong way to be going about it.
Languages like Go and .NET, through the Blazer Project, are both trying to build this thing where you don't have to write any JavaScript and you can build these really rich applications that run in the browser.
I remember another platform that did that.
It's called Flash.
Didn't work out so well for them in the end. But really, I don't see it as a final replacement for JavaScript.
We will still always be running JavaScript because at the end of the day, browsers are very good at executing JavaScript, and JavaScript is very good at working with browsers to do things like DOM manipulations.
A lot of stuff when we're working with the DOM is working with strings, and as I mentioned before, WebAssembly doesn't have strings as a data type. It just doesn't exist.
We've only got numbers.
It's very difficult to represent a full DOM as int 32. It's gonna be not highly accessible.
But there's certain things that WebAssembly will be very good for.
Just some terminology so that we're all on the same page as we talk about WebAssembly. We have our WebAssembly module.
This is the binary that we get from our compilation step, whether it's output of a C compiler or a Rust compiler or whatever it might be. This is the thing that you're gonna fetch in the browser or load in a node.js runtime that will be executing our WebAssembly application. We then create an instance of the WebAssembly virtual machine using that provided binary. We can create multiple VMs inside of a single application, so the browser can spawn as many Assembly VMs as you want. Just you've got to make sure that you think about the performance overheads of spinning up a whole lot of little VMs inside of your browser. But you can definitely do that.
We can, we then have memory.
Now, because we control this virtual machine, we control what it is aware of about its host, so we control the memory that goes into it. We've defined memory as essentially an array buffer in JavaScript that we can pass into our WebAssembly virtual machine, and we define that in pages of 64 kilobits, minimum of one, maximum of however much memory you can dedicate from your machine to it.
But we can also share memory across VM instances. So if we have, say, three different little WebAssembly VMs that are running, they can actually share that memory, and we can then pass values between them quite easily. Because memory is used to pass more complex data types back and forth, so if we didn't wanna expose a string, if we can't actually directly work with the string, we have to return that through memory or we have to put that into memory and read the memory buffer.
We can do that and we can then pass those values between our WebAssembly and our host in JavaScript or WebAssembly and another WebAssembly instance. Finally, we have a table.
A table is kind of like memory, as well, except the table is used to pass function references, not value references.
And this is useful 'cause if you've got a function that you want to expose from JavaScript or you wanna dynamically expose functions from JavaScript into WebAssembly, you put the reference to those inside of that table, or if you wanna do that in the inverse, expose from WebAssembly back to JavaScript, we push them into our table so that our host can access them.
We can also expose functions from one virtual machine to another virtual machine using tables, assuming that they're using a shared memory, or shared table inside of our VMs.
Okay, but why? Why are we wanting to use WebAssembly, or why is it something that could be of interest to us building applications? I think the idea of sandboxing is probably the most compelling reason to use it.
We're building stuff in the browser and it's getting more and more complex, and if it's all running in a shared user space, we have the risk of things like cross-site scripts being able to access values or information inside of our applications that we might not want exposed. Maybe we're building an application that has some level of cryptography that it does in the client.
Well, do we want that running in the same memory space of potentially untrusted code? So we can use this to sandbox things withinside of our browser runtime.
We can also share code between our client and server. While it might be nice to think that we could live in a world where absolutely every bit of code that's ever written is written in JavaScript and that's running in node on the server and browsers on the client, but the reality is that not ever application on the server is gonna be written in JavaScript, because JavaScript isn't always the best language to solve our problems.
So maybe we've got a part of our application that we want to use withinside of our client. Well, we can write that in JavaScript, and we could write that in our server-side language. We have Go, Rust, CPython, whatever it might be.
But then we've got two implementations of it, and we might need to, you know, fix a bug in both implementations, or we might have some slight variations and differences in how they operate because of the way JavaScript works versus another language. I think a really good example of this could be doing image manipulation.
Say in their browser that we've got, we're allowing people to upload a photo, and then we're gonna do some manipulation of that on the server to save into a storage location. But we wanna give them a preview of what that will look like before they hit save and we do the cropping for them or we do the resizing or whatever it might be. Well, if we've found a image manipulation library that we really like, but it happens to be written in C++, well, we have to find someone who has either implemented that same library or that same set of functionality in JavaScript so that we can run it in the browser, or we can take that C++ library, compile it for WebAssembly, and run that in our browser, and then we can also use it on the server without having any kind of difference.
And complex mathematics.
We learned in our last talk that there's some fun nuances around the way that JavaScript handles numbers, and WebAssembly is very well designed for working with numbers.
That's its only data type.
So if we wanna do something that's got complex mathematics. Maybe we're building some ML models.
Well, that might be better suited to be done withinside of a WebAssembly application than it is to be done, or a language we would compile to WebAssembly, than it would be to do just plain old in JavaScript. Similarly with cryptography, gaming.
A lot of the really early demos of WebAssembly and its predecessor asm.js was around bringing game engines to run in the browser. So that's where I kinda see WebAssembly playing out. It's not the next version of a React is getting written in WebAssembly, but it can be used to supplement our applications. So what's it like to build a WebAssembly application? Well, the first thing we're gonna need to do is pick a language that we wanna compile our WebAssembly programme from, and we could do something like C or C++.
Those were a lot of it was designed around. We could use Rust if we wanna be a little bit more hipster than just using C, 'cause that's what cool kids are using, apparently, for high-performance computing these days.
We could do something with .NET, C# or F#.
Both have compilation targets for WebAssembly. We could write the WebAssembly Text Language from that first set of slides, but that would be a little bit difficult.
But when I started exploring this and working out where I could use it, I decided to go with Go, because I wanted to learn Go and this seemed like a great way to do it.
So I built a little Go application that is designed for bringing a list of conferences from a markdown file that exists online.
I'll just show you how this works.
So this is just a console application that I've got running here.
It dumps, it parses a markdown file.
It then exports that as, it reads the syntax tree of the markdown file, does some manipulations to that, and then just dumps that out to my terminal, and I can see a whole bunch of events that are happening around Australia.
Here it is.
This is all on GitHub and the links will be available afterwards. And also, I'm not trying to convince you to write Go lang or anything like that.
I'm just using this as an illustration of building a WebAssembly application.
But this is my application.
It's doing a, runs a HTTP get to download the file. It then uses a little library that I wrote that walks the syntax tree using a parse markdown function. It will then walk the syntax tree, build up a new object type, and that will be then sent down to, or return from this function, and then I'm just serialising that as JSON to dump to the console, because that was the easiest way for me to represent it. So this is all quite useful 'cause I've got this application that now works, but I wanna convert this into a web application, or I wanna use it in a web application.
So I think about the kinds of tools that we build web applications with, one of those being Webpack.
Because, well, I need to do a compilation step of my Go lang code down to WebAssembly binary. And when I think compilation, that's a really good fit for what Webpack can be used for. It can compile another language to JavaScript, or it compiles a bunch of other bundles to be used from a JavaScript application.
I then need some kind of UI that I wanna build this on top of, because I wanted to treat this as a web developer. I'm not trying to be a Go lang developer here. I'm trying to extend a web application with some Go lang functionality.
So I use React.
It's just my library of choice.
But that could be, you know, it could be View, Angular, or raw HTML.
Yeah, but again, I was looking at the kinds of tools that I use as a web developer.
So what's, so let's bring this all together and have a look at how that works.
Jump over to my other one here.
I have a React application.
It just is a fairly basic React application that does some mounting of components which then goes out and then wants to download a bunch of markdown files.
So we go down to this downloadYear function. It uses fetch in the browser, because the browsers are very efficient at downloading files, so I thought I'd use fetch rather than, like, putting that, there's no point putting that inside of my Go lang application.
You know, the browser knows how to fetch a file pretty well. So I can do that, and then I have to get to this Go lang application. So I start with import something from a .go file. Interesting.
Now it's just like any other web application or any other bit of code that I could be importing. I could be importing an SVG or a CSS file, TypeScript, JavaScript, whatever it might be. It just so happens this one's Go lang.
And then have a Go lang file here that calls to that markdown tools, which does the syntax tree walking and then generating the object model.
The only difference here, instead of just returning the objects, it serialises it as JSON so I can then consume that in the browser, and I registered that for use withinside of my web application.
And it's exposed asynchronously, so I can use promises against it.
Just treats it like normal working against a web application.
And then I define a loader inside of my Webpack config as a loader that I've written to do that, and it all works nice and seamlessly together. And then I end up with a web application that looks kind of like this.
My design skills are top-notch, as you can see here.
But what I've got is, if we see here, is a whole bunch of events that are happening around, in 2019 around Australia.
I can then filter them by state.
But what I'm doing now is I'm working with a JavaScript object model. This is just, I'm just working with a React application. This is doing state changes to my underlying data set, which just so happened that that data set came from WebAssembly rather than something that I actually created entirely from the browser. Now, I could've done this, there's plenty of markdown tools out there for JavaScript that would parse and walk a syntax tree and things like that, but I just, I thought this was a good way to sort of demonstrate separation of concerns, as that's just a bit of business logic that I could ship off somewhere else.
But then this is just a single-page React application. So just to wrap up, that was a very quick look at WebAssembly, I'm aware. But WebAssembly, it's real, it's here.
It's in all of the browsers.
It's been in Chrome for a while now, which means that it's in the Edge version of Chrome. There's some rudimentary support for it in Edge, not Edge Chrome.
I've found some issues particularly with the way that you have to instantiate larger binaries, which, something like Go lang, CPython, and .NET, they produce much larger binaries.
I think that application that is running just to parse the markdown is around about four meg. So this is not for your, like, really super small applications.
You will often end up with quite large WebAssembly binaries. So all the evergreens on the desktop work fine. It's kind of hit and miss with mobile browsers. Chrome on Android is kind of the most up-to-date one because it's so close to Chrome on desktop. I haven't had a lot of chance to test with iOS 'cause I just actually don't have an Apple device that I can test with, but I would expect that that's not too far behind 'cause it has reasonable support with Safari, as well. Use WebAssembly not to replace what you're building at the moment, but to extend it.
Sandbox parts of your application that might need to be sandboxed or could benefit from being sandboxed.
Push things that you would maybe have done only on the server because it was too hard to implement them in JavaScript to the client if you're using a language that can compile to WebAssembly. And that list of languages is growing by the week. Look at how you can reuse stuff from server and client. If you're sending stuff over the wire, maybe there's an opportunity that you can have those types defined and compiled in WebAssembly so you can do type validation on server and client without actually having to parse them or just assume that the data structure is the same. Because at the end of the day, JavaScript might be a great language, but it isn't the only language and it isn't the best fit for every single problem. Certain languages are gonna be a whole lot better for certain problems.
High-performance concurrency, cryptography, stuff like that, things that are very centred around mathematics and number operations are better done in languages other than JavaScript.
So consider putting those workloads in a language that can be compiled to WebAssembly and then just call those binaries as a WebAssembly application to extend the application that you already have running inside of the browser. Just wanted to finish with a couple links that might be of relevance.
So the journey that I went on from someone that had no idea about WebAssembly to building that application that I just had there, you'll find that at the top link, aka.ms/learn-wasm, and there's a heap of great resources on webassembly.org that's behind the specification.
I'll also tweet out these links, as well, with the code hashtag, so don't feel like you actually have to write them down frantically before I click to the next slide or blank it so that you just go, oh, I was, what was that last letter? M.
But WebAssembly is here.
It's a really interesting bit of technology. I would highly encourage people to experiment with it, see where it could fit into the kinds of applications that you're building, and think about what it can be used for for the next phase of our web applications. Thank you for listening.
(audience applauds) (mellow music)