 Hey there, I'm Ben Morse. I'm a developer advocate working on the web and on AMP. And I'm Crystal Lambert, technically a writer, working for the web on the AMP project. We're here to talk about something we think is pretty cool, a new way to run JavaScript in web workers with AMP. Awesome, let's get started. But Ben, what is this slide? JavaScript foe? I love JavaScript. It lets me do whatever I want. Sure, JavaScript is amazing. It's made the modern web possible, but we both know that many websites are too slow and that's partially caused by lots of JavaScript. That's one of the reasons why people like this are staring at their phones, waiting for our sites to load. Yeah, that's no good. You'd think the more JavaScript, the better. I could write more code to make things quicker. Well, it's like too much ice cream or time spent at home. You don't want to overdo it. Well, what about these web workers? I hear you can use them to get JavaScript off the main thread, but I'm not sure how to get started. Yeah, it can't be pretty intimidating because the thing is Oh, and another thing. AMP doesn't let me write my own JavaScript, period. Can we make a video about that too? Well, conveniently, Crystal, this video can be about both those things because AMP now provides an easy way to use workers. So we're going to show JavaScript developers how AMP makes it easy to try web workers. And for people who are already using AMP, we'll show you how you can write your own JavaScript without breaking AMP's performance guarantees. For everyone, it's nice way to run JavaScript in a way that's unlikely to harm your web vital scores. Oh, yeah. I'm hearing lots about this web vitals. That's our page's first input delay, largest contentful page, and cumulative layout shift, right? Those are the three. So let's get going. Another slide. What is this? A guy knitting? Yeah, it's a transition slide. Well, it does remind me. Why is the web single threaded? I mean, every modern OS has multiple threads. Why hasn't the web caught up? Honestly, it's just how browsers and JavaScript have always been. I mean, of course, modern browsers can multitask. They can't be more than one thing at a time. But each browser tab has a single thread for the UI. Only one process can make changes to the screen at a time. That means JavaScript can block the browser from doing things and vice versa. But wait, JavaScript is asynchronous, right? So whenever an event gets fired, doesn't the event handler's code start running right away? Well, sure, but all of the code on the web page still runs in a single thread. This diagram illustrates JavaScript's event loop. So the browser fires an event. If you have an event handler, that code runs until it's done. As other events fire, they get added to a queue. I see. So if my code is handling one event and another event fires, the browser just can't spin up another thread. Instead, it has to wait for that event in the queue. Right. It has to wait until the current code is done. Let's say the user taps a button while your code is running a long task. Well, a JavaScript can't handle any other event until your task completes. So the next bit of code will be delayed. Worse still, the browser may be unable to change the UI because it's waiting for your code. I guess if it weren't that way, everything would just be fighting for control over the DOM. And you'd have race conditions and general chaos. Oh, yeah. And unfortunately, to make JavaScript thread safe, you'd have to completely rewrite it. All right. This is making some sense. Not only can excessive JavaScript make your page slow to load, it can also make the page slow to respond to users' interactions. I'm guessing this is where WebWorker is coming. Yes. JavaScript in a WebWorker runs in a different thread. And this is not a new idea. WebWorkers have been around for about 10 years. You're kidding. 10 years? That's longer than I've been working on the web. Why am I just learning about them? I think because their limits have made them harder to use. Workers can't cause race conditions with other workers or the main thread because they lack access to the DOM or the global scope. Instead, a worker communicates with the main thread by passing messages back and forth, where each message contains an object. There are libraries that make this simpler, notably CalmLink by CERMA and Workerize by Jason Miller, but workers can't access the DOM. So workers are great for doing long tasks off the main thread. But what if you want access to the DOM? That's a big obstacle. And that's where AMP script comes to the rescue. I knew at some point we were going to bring AMP into this. We did. So in 2018, the AMP project released an open source library called WorkerDOM. WorkerDOM makes a copy of the DOM for the worker's use. WorkerDOM also recreates a subset of the standard DOM API. This lets the worker manipulate the DOM and make changes on the page using standard techniques. WorkerDOM keeps the copy of the DOM and the real DOM in sync. So when something changes in the real DOM, WorkerDOM sends a message to the worker to make that change in the copy. And if your worker changes its copy, WorkerDOM sends a message over to the real DOM and the same change gets made there. So I heard you say AMP. Is all of this only true for AMP? Or can I use WorkerDOM with a different stack? You can import WorkerDOM into your own project. But WorkerDOM is super useful for AMP since it provides a way to run JavaScript in a sandbox where it can't run rampant and break AMP's performance guarantees. AMP encapsulates WorkerDOM in a component called AMP script. This is a little abstract. Can you show me some code? Code, I understand. OK, fine. Let's make a basic Hello World example with AMP script. In the body, we insert an AMP script component. The DOM it contains gets passed to the worker. So here to the worker, that entire DOM is that H1 tag. Next, we put our code in a script tag. Whoa, that's weird. You set the type to plain text instead of text JavaScript. Yeah, we did. That's so the browser won't see it as JavaScript and just execute it immediately. Instead, AMP script finds the code and puts it into a worker. So the code in this script here grabs the first H1 tag in the DOM and appends a comma and the word world right on page load. And does that work? Look, magic. That was pretty quick. Let's watch it again. I'm overwhelmed. Well, OK, it's not Gmail, but that world was really and truly added by a web worker. Can you prove it? If we open DevTools and go to the Sources tab and click over here, we can see our script right under the code added by AMP script. OK, that's kind of cool. Here's how that looks in a full web page. I have left some things out for simplicity's sake, but you can see that as with all AMP pages, we're loading AMP's runtime script. We're also including the JavaScript that makes AMP script work. So do you always have to include your JavaScript inline like that? It's not really a best practice. Yeah, that's a good point. We can also store the JavaScript in its own file by using AMP script's source attribute like this. So that example works, but it's not really that useful. Could we say add that world when the user presses a button? OK, fine. Let's add to HTML a button that says, hello, who? We'll write JavaScript that grabs that button and adds a handler for the click event. When you click the button, it works as magic. Let's try it out. So there's hello, there's our button. And look, hello world. OK, let's go a little crazy. Super neat-o. What else can we do? Does AMP script let us do a fetch? Does it ever? Here's that hello world example modified to retrieve the word world from an endpoint. Workers natively support fetch, XML HTTP requests, and even web sockets. OK, this is getting pretty cool. But this is AMP, right? How does AMP just let me write any JavaScript I want? Well, that's a good point. AMP tries hard to guarantee low cumulative layout shift to keep page elements from moving around. If your code makes mutations to the page that would really disturb the page layout, AMP reserves the right to disallow those changes or even shut the worker down. If your AMP script container can't change size, it can't disturb the page as much, and it gives you more freedom. That's why I specified the height and width here in the HTML and why I didn't choose AMP's container layout. There's a lot to this, so check the documentation on amp.dev for details. Hold on. Can I just use AMP script to inject more scripts into the DOM? Nope. You're working with a virtual DOM. Not going to work. Fair enough. But I see something about not allowing more than 150 kilobytes of JavaScript. Is that on a page level? That's right, that 150k is per page. But I could still fit jQuery into that. And oh, I can just copy in my favorite image slider and charter libraries. Well, remember the worker DOM has recreated the DOM API that supports in its own JavaScript. If worker DOM supported the whole DOM API, it would be cumbersome and huge. It would slow down pages enormously. So pretty few third-party libraries are going to work right out of the box. OK. Then what's the best way to use AMP script? Well, one way is to use vanilla JavaScript while keeping an eye on this table of supported APIs. There is quite a bit there. Wait. React. Can I use React? Yes, that's the other way. React uses a very specific subset of the DOM API. So the worker DOM team made sure that subset is well-supported. OK, but I've used React before. My React bundle might need to break that 150 kilobyte limit. Yeah, that's why it's probably better to use React instead. React is highly compatible with React, but it's only 3K minified in GZip. For projects with more code, React is probably the way to go. Here, I've made the button example using React. I find it easier to write the debug, the JSX, in a simpler environment, and then build it into my AMP page. So let's build this. Let's start up our server. And there's our page with our button. It works. All right, that was a lot. If only there was an AMP script tutorial out there. Wait a minute. Didn't you and I already make one of those? Yeah, you want to take the next slide? Of course. That tutorial is a great introduction to AMP script. Head on over to go.amp.dev slash learn dash script to get started. And then keep on going. Remember that worker DOM is still quite new. If you have future requests or find things that you're missing, please get involved on GitHub. Help improve it. In conclusion, web workers can help you keep JavaScript from slowing down your web pages. AMP script is a nice way to try this technique out. You can find all the code from this talk here on Glitch. Thanks for listening, and let's get to work on putting workers to work. For you. Ah.