why you're not just using a straight up property is because we want to tie logic to the fact when somebody changes the value. Exactly, right. So if it was like number dot is, I can never get that right. Percentage, you could just be like, I don't know. Whoa, you could be like that. Throw new error. No. See? See, people don't, there you go. Validation, and also helpful error messages. Well done. Hey, I am all over this. Don't worry. I'll tidy it up before it goes on to the GitHub repo, which it will. Yes, our GitHub repo. By the way, it is Google Chrome slash UI element samples. All our previous code is on there. This one will get up there at some point today when we're done. Right. And feel free to use it and play around. Yep. So when you set the percentage value, so we're going to get a reference to the dial from outside. We're going to say percentage equals 1 or 0.5. We don't want to draw, which we'll draw on the canvas. I wonder what the draw function does. Oh, I don't know. Let's see. So what we'll do is we will begin. So you always operate against the context. So we'll begin path. And then I always say, if you're going to do something like begin path, always use, or is it close path? Always put its corresponding. Because the opposite of begin is close. Is close. That makes sense. Welcome to the web. I know, right? So we want, I'm going to do like a circular dial, right? So I want an arc. So like a normal progress bar, but bend into a circle. Yeah, all the way around. So I love this. So I've got to do x, y, radius. OK, fine. So I'm going to say mid, mid, and mid. Now, I haven't defined mid yet. So that's fine. Zero. Let me guess. It's the middle. Oh, you're so good at this. Fine, fine, fine. 0.4. I'm just guessing an angle, which is in radians. Just because I want to check that it draws something. Because when you're working with a canvas, sometimes it draws nothing. And that's fun to debug. Yeah. So const mid equals scdow. So we're going to go for the size. I'm going to hand tune this. We're getting some good mileage out of the constant. We're getting some good mileage out of the constant. I know, right? It's all over the place. It's going to be great, this.  Shush. Shush. See, that didn't draw anything. Do you know why? Because after you've closed the path, you actually have to say to the context to fill the path. Yeah, because path just defines the path. And you have to say, what do you? You can stroke it, or you can fill it, or you can probably do both, I think. Do you know what you need to do as well? You actually need to call draw.  There we go. Hey, we've got something drawing, at least.  Now, the thing is, that's not really what I wanted. Because I want to kind of pie kind of.  Yeah. It's correct. And as always, you did the mistake, not the computer. Thanks. So the fix for this is to actually move your pen to the middle, like you were saying before. It's like, think of it, the context is like a pen. So if you move the pen as it were, and you say move to inside the begin path, and you say mid, mid, it draws the kind of pie chart. That's good. Oh, which is good. So now that's pretty good. So what else are we going to do? Let's see. Let's call this the outer arc. I mean, I find it a little weird that it kind of starts to the right, because we all learn to read the clocks. I feel like. OK, fine. Yeah, that's fair enough. So yeah, he's right. You wouldn't normally go out to the side, would you? If you're going to start progress, you start at top middle. Now, one option here would be to take this start angle as being 0, and just do like minus 90 degrees. But in the interest of showing something else to do with the canvas, I thought I would do it like this. We do a rotate. We rotate the canvas, which is going to rotate the coordinate system, and I'm going to do it right. So yeah, the coordinate system basically gets turned. Minus SC dialed up. Let's see, I'm such a grown-up. 90 degrees. Really? Concentrate 90 degrees? Look at that. I was like a proper. But half doesn't get it on constant. No. I mean, no. Return math.py times by 0.5, right. Didn't we just say sometimes the canvas draws nothing? Yes. I know what this is, because I've seen it before. It's because we're actually rotating around the top left hand corner. We're actually rotating around the origin top left. Oh, so we have this thing here, because we're rotating it out of view. So we're drawing up there. Cool. It's just to help people. So you're basically just going to move the origin. Yes. And then go just. So yeah, if you move the origin with a translate, and if we move it to the middle, middle. So we move it to the mid, and then we rotate, and then we move it back again. It sounds odd, but it will work. It makes sense. But otherwise, you would have to. Well, I don't know how else you'd do it. It is. It is right. So now? There you go. Yes. That's good. Here's another thing. I think what we'll do is we'll do an inner arc while we're here. And then we're going to actually wire it up to your code, which is now legacy code. Ew. We started out at bleeding edge, and it's already obsolete. No kidding me. That's yeah. Right. So math.py times by 2, because we want a full circle in the middle. I think it went full circle. Ooh, ooh, ooh, ooh, ooh. This will be fun. This won't get anybody upset. What? We'll call it tau. You flame-baid. Apparently, this has support in this room. Some mathematicians are like, yes. You should use tau, which is the same as 2 times pi. Trolling. Right. So we'll do this.ctx.fill, fill, style equals, let's do a white. Let me go. I'm going to need a fill style here. So for now, I'll set that to back to the black. Now, that doesn't look great because it's the same radius. I mean, no. But if I do mid times by 0.8, look at that. I'm now getting something. Oh, nice. So we get like a little thing that. We're good. We're good. Now let's wire it up to your stuff. Oh, yeah, let's pipe in actual the loading percentage. So we'll do percentage, which will be a value between 0 and 1, times by scdial.dial, which will disappear because the value is 0, which is fine. So actually, this time it's working as intended, even though we don't see anything. Absolutely. So over here in your thing, I'm going to get reference dial equals document.quick. And this is where we can say this is not production code, because now we're kind of mixing concerns. My module was supposed to take care of loading. And now we're also kind of like patching UI stuff in there. But for the sake of how to do this, it's valid, I think. Sure. Keep going, keep going, keep typing. Stop. OK, so if it's done, well, we know that dial.percentage is 1. That's fine. That is correct. And for everything else, we actually want to know how many bytes have come through the wire. So we need the total bytes, so let's say bytes total. The HTTP protocol can help you with that. Yes, it can. With the headers.get, and then we're going to ask for the content length. And then I'm just going to parse in on that. Yeah, because it's a string. And if you divide by string, things are not fun in JavaScript. Right, so we know the total number of bytes if that header comes through, which I'm relying on. Let bytes counted be zero. Sure. Now, if we get down here, we're going to have to say, we're going to add on to the back end. Right, so we can just increase by the string length, because it's JSON that is equal to the number of bytes. Because there's no unicode things. Yeah. Right, it's counted over bytes total. Yeah? Should we divide between zero and one? All right, looks good. Yeah, that's not.  Sorry, just for the just. We could just say this is intended and ship it. Sure. Do you know what's going on here? The canvas is a state machine, effectively. Every time we call draw, it's rotating the canvas by 90 degrees. Oh, so we keep just rotating and drawing. Yeah, that's obviously the drawing. So what we can do here is we can actually ask, before we do that, the rotation and so forth, we can actually ask the canvas to save its stuff, its state, basically, like what's the fill style, everything like that. We can save it. And this is another one of those moments where if you call.save, you immediately figure out where you're going to call this.ctx.restore, where you're going to bring it back. Because otherwise, you're going to push something onto a stack, and you're never going to pop it. And that won't be fun for you. Oh, so this will basically revert to the previous state, except all the drawing. It has happened in the meantime, and will persist. So here, we should now go round. That's definitely better. But actually, you probably can't tell. I'm going to zoom in. Oh, that's very pixelated, isn't it? It's really jagged. And the reason is because you also have to clear the canvas. Oh, so the canvas, by default, draws actually smooth, which is why this is surprising. But we keep drawing over and over the smooth edges, so it adds up to not smooth. So we'll do the size, and we'll do the size. So we're going to clear it. This has to be one of the most efficient constants ever created. See? Right. Much smoother. Much smoother. Now, while I'm here, since it's not in the middle of the screen, I'm going to move it to the middle of the screen. And I do like doing this. Style, style. There you go. H, M, L, body. Let's do it. I do that all the time. Ah, you want to. Oh, are you going to do the master discipline of CSS of vertical centering? Yay. That's good. When people are like, you can't do vertical centering in CSS, you can say, sure you can. Display flex. Align items, center. Justify content, center. Smile a happy smile. Hooray. Right.  And I'm going to do small. I was about to say, it looks a little bit on a chrome. All right. RGB128, there we go. That's a blue color. But let's make it nicer. Let's see, let's do this as a template string. I'll stop it, Paul. There we are. Told you I made typos. Let's do so. We'll do what? This dot percent, whoa. That would be interesting. Yeah, that would be weird. Times by 255. Oh, so we're going to go from blue and no red to blue and full red. Except we're going from black to pink. Because RGB values have to be rounded. Oh, so once you have decimal points, CSS goes like no black. OK. OK. So we're doing all right here, aren't we, mate? That's all right. How are we good? What else can we do? Oh, we could put an actual number in the middle. So to tell you how far we've actually got through the downloading. Let's do that here. So put label, label, label. See how I'm actually doing comments? Because I know when you come back to Canvas code in order. Just like half an hour after this, and we're going to try to upload this, we will have forgotten what it is about. Yeah, it's right after regular expressions where you're like, really? It's like just face on keyboard, and it still kind of works. Brings out my prettiest faces, that one. Right. OK, we're going to do fill text. So we know we want to do that. We should set the color for that. ccx.fillStyle, yeah. Equals. Let's do it as a kind of gray color. All right. Fill text. If person would prefer 333, honestly. Really? OK, fine. Fine. You're not normally that bothered by this kind of stuff. Fine, 333. Slightly darker gray. Percentage times by 100. And we'll do it at mid-mid in the middle of the dial. Sure. Sounds good. I mean, I would ship it. Sure. Do you know what? This is actually a great opportunity to talk about the restore. Because if we do the restore before the label. Oh, it's still turned. It's still turned. So we'll do that. That'll put it back where it needs to go. All right. That's a start. And then we can do, let's see. We should probably round this too. Math.round, yeah, I think you're right. I don't, people don't care about, like, the 15th decimal place. I know, right? It's tiny. Look at how tiny that is. OK, let's make it bigger. This CTX dot font equals, let's have a look. We would want to do, let's make it one of these. And we'll say, oh, no, wait, SC dial dot size. See, it's not so useful. Oh, because you'll make it dependent on the, that is good. Quarter the size, PX aerial. Sure, that's going to be fine. OK, sounds good. Symmetry, I mean. And the thing is, you don't handle negative feedback very well. The thing is, I feel like the bottom left corner of the one is actually perfectly centered. Yeah, it is actually. It's because the text baseline and the text alignment will mean that it is for the bottom left hand corner, which we can change by saying this dot. I thought we'd have to, like, measure ourselves or something that seems doable. That would be awful, you can imagine. No, we just do this, text baseline. I like how one is middle and the other one is centered. Yeah, would be too easy. So Flexbox is a center, center. SVG, yeah, let's not talk about it. There you go. Round and go. But I think what we can do is to turn ourselves onto the label, because we can. I'm going to do this one at 777. Is that OK? All right, I'll let you have it. I mean, OK, let's do this at, like, oh, I don't know. 0.06. I love just guessing numbers. I don't think it, yeah, you can just. Like, number phishing is great fun. You're, like, 0.06. Yeah, why did you stop using, like, constants now? Huh? Huh? I don't know what you mean. All right, percent. That's rubbish. Great stuff. Let's do minus 20 plus 15. Sure. Ooh, close, but not quite. Minus 20 plus 20. Close, but no cigar. There we go. No, I don't like that. Looking good. When is 14? Go on, then. Plus 26. Sure, why not? Why not? I mean, it looks good. I feel pretty good about this. Can we do this with network throttling? Just to have, like, a little bit more time to explain. What? Network throttling. Oh, sorry. More time to explain. Sure, yeah, absolutely. Yeah, because at the moment, this is no throttling. So imagine, then, we were to click on that, and it was regular 4G. Now, this is actually really important, because we're now not blocking, as Serma's code showed you, we're not blocking on this stuff coming down before we show anything. And we're actually able to give the user some kind of information. And if you needed to do something with those tweets, like jason.parsing them, showing them in IDB, something like that, you could be doing that kind of work here, too. We could already be rendering parts of the tweets, because every chunk contains one tweet. So we could start listing things while there's a progress bar in the top. And the main thread is still available, as you can see, because it can draw. Let's do that. In fact, we have just about enough time, I think, to get away with this. So on the connected callback, we couldn't call draw. I'm going to do a request animation frame to call this.draw. And I'm not going to call it anymore. So it's going to go into this kind of busy, it's just always going to draw. And it's going to probably break. I'm not going to spend ages explaining why. It's because the request animation frame, when it's called, this stops referring to the class instance and starts referring to window. So the way to fix that is basically. It's the Lewis bind, as I call it. Yeah. Dot bind. It's weird. But we cover it in every single supercharge, because I always kept doing it. And since that's drawing every frame now, we should be able to do a recording here. And we should be able to see that this is, if it works. It's comfortably at 60 FPS. We have loads of head space, head room, in our per frame budget. And we're getting something to our users really fast. And we are out of time. But just a reminder, we've covered streams, transformed streams. We've covered custom elements. We've drawn Canvas stuff. We've got you something which is taking 16 megs of JSON. And I think we're in under 200 lines of code or something. Yeah. Let me just check. This is a reminder of just how awesome the web platform can be. Because you've got, what, about 110 from me. And you've got about 70 or so from you. So that is about 180 lines of code. And we've got something that I think is reusable and quite interesting. So there you go. Thank you very much for joining us today. We've had a great time. Let's have a little chat about a brand new API that's just coming out on the platform called the Media Session API. I heard about it when I started doing this app. It was mentioned to me by a couple of the Chrome engineers. But Francois, who's on my team, there's a brilliant Google web developer's update where he explains in complete detail about how to set up the Media Sessions API. So check the notes below. We're going to pop in a link for that. But I will just show you briefly what it actually looks like in the context of the app on the phone here. In fact, because you probably can't see it, let's switch across to the direct screen cam. There you go. That's what it looks like on the phone. So as I start playing a video like this, la, la, la, you see that if I swipe down from the top, we actually get a notification which has an icon here and it has play and rewind and fast forward buttons. And you get to configure those yourself. In fact, let me go into another one of these videos where I think I've actually set up to load some custom album art as well. So there's me and Jake. And you can see here now we've got custom album art, which is the picture of Jake. And the previous and next, the fast forward and rewind buttons are actually set to be skip forward 30 and go back 30. So I should be able to tap that and go forward 30, which you can see there. Oh, it's just skipped it right to the end. Whoops, a daisy. But I can replay the video. Don't worry. The other thing that it actually does, which I really like, is if I turn off the screen then on again, you can actually see that the picture, the album art is actually the picture for my phone in the background. That is very exciting, isn't it? So let me show you a little bit as well, since we're here. Let me show you a little bit of the code. It's very straightforward. We have a quick check whether we support the media session API, which is basically looking for, let me show you actually, it is just simply looking for media session in Navigator. And if we have media session in Navigator, then we consider ourselves having the media session API. And what we do is we say navigator.mediasession.metadata. And then we create one of these new media metadata thingamajigs, which is very exciting. ESLint doesn't like it. It doesn't think it's a real thing. It is a real thing. You can totally use it. And you give it things like the title, the album, the artwork. I've only set the 512 and the 256. But very much like your manifest files for progressive web apps, you can set as many of these as you need. And the user agent will choose whichever one it thinks it makes the most sense for the device that it's on. And it will upscale and downscale as necessary. But I currently am just setting a couple of them. I may need more as time goes by. And then afterwards, after we've set up the metadata, we set some action handlers for things like the play, the pause, the seek backwards, and the seek forwards. The thing to bear in mind here is that any that you set will have the buttons appear in the notification. If you don't set one, because there are other ones that you can set as well. And I forget which ones they are, but check out Francois' post. He explains the whole Kit Kaboodle. Any that you don't set won't appear. Any that you do set will appear in the notification. And then somebody can control your stuff from the lock screen or by just dragging down from the top. All very good, isn't it? And very straightforward code to be writing. So a brilliant little progressive enhancement thing that you can chuck on and that I have chucked on my media app. Cool, totally. Hi there, iOS developer. Interested in getting started with the Firebase platform in your app? Well, you've come to the right place. There are two main parts to getting the Firebase platform up and running, adding your app to the Firebase console and installing the SDK. Let's go over these one at a time. For starters, let's go to the Firebase console at this URL here. Depending on when you're watching this video, the UI might look slightly different, but the general concept should remain the same. Now, depending on your situation, you might see a blank create a new project screen or you might see a list of existing projects. Oh, before we go further, let me take a moment to explain the difference between projects and apps. A project consists of one or more apps. Now, all the apps in the same project use the same Firebase database backend. And if you want, you can use features like Firebase Cloud Messaging to talk to all of them at once. You don't have to, but you can, which is sometimes convenient. So if you're a developer that has a cross-platform app, you generally want to put the iOS and Android versions of your app in the same project.  Your user can access the same data if they switch back and forth between the iOS and Android versions of your app. Things like dynamic links will work across both platforms. You can send one notification to all versions of your app and so on. On the other hand, completely different apps, you should put those in completely different projects. There's nothing gained by cramming them into the same project except tiers and heartache, I guess. So if you're working on a cross-platform app and your Android or web team has already created a Firebase project, you should probably select that project and connect your iOS app in there. Otherwise, if you're the first one to be adding Firebase capabilities to your app, you could be the one to create the new project. In my case, I'm the first app associated with the project, so I'm going to create a new project here. I'll give it a name, and there we go. Once you've selected or created a project, you're going to want to connect your client app. I'm going to select the iOS button here, and I'll give it my app's bundle ID. You are eventually going to need to add your app store ID here if you want features like Firebase invites or dynamic links to work, but you can leave this blank for now and change it later. Now, when you click Continue, your browser should automatically download this Google Services Info.p list file for you. Note that it needs to be named this exactly. So if you get that little one in parentheses after the name like I just did, you're going to need to do a little bit of renaming in Finder. OK, next up, drag the file into your Xcode project, like so. And let me go back to the console and hit Continue here, and it's telling us that this would be a good time to install the Firebase Cocoa Pods. Now, I'm assuming you know something about Firebase Cocoa Pods, but if you don't, here's a little video for you to check out. It's fun. So I'm going to jump into my project directory here and do a little pod init. We'll open up the file, and I'm going to uncomment this line because I am using Swift and this line because my app happens to have a base SDK of 8.0, although at the time of this recording, Firebase is supported as far back as 7.0. Next up, let's add some pods. Now, it's important to remember that to keep your app nice and spelt, you should only install the Cocoa Pods for features that you need. In fact, there is no all-encompassing Uber Firebase Cocoa Pod that installs everything for you. You're going to need to pod install each individual feature, and you can find a full list of the Cocoa Pods and the features they correspond to over here. Now, for starters, I'm just going to add a Firebase slash core, which includes everything needed to get the basics up and running and also enables Firebase Analytics. So now I'll make sure my project is closed and I'll run pod install, and then let's open up the generated workspace. All right, we'll build it and make sure everything compiles OK, and it does. So we can move on to the next step. OK, it looks like the last step here is to add some initialization code. I recommend putting this in your app delegate did finish launching method. First things first, let's import Firebase in my app delegate. Note that this is usually the only thing I'll ever need to import no matter what we've installed. We're doing some pretty nifty work behind the scenes to make sure this works properly. And I know this sounds like the exact opposite of my only pod install what you need advice from like two minutes ago, but trust me here, this makes development a whole lot easier. All right, so next up, we'll add the line for app.configure to make sure Firebase gets set up properly. And that's actually all you need to do. This configure method will take a look at what libraries you have installed, initialize them, grabbing all the appropriate constants from that Google services file that you dragged in earlier into your Xcode project. So we'll give it a quick run. And if everything is set up and working correctly, you should see a few lines in your console about how Firebase Analytics is up and running. All right, so congratulations. You are now up and running with Firebase. So there are a lot of places you can go from here. You can add sign-in using Firebase Auth or get your app talking to the real-time database, or start tracking more of your user's usage with Firebase Analytics. You can check out these links to get started and have a little fun. I think that one of the things that I have applied in my life as I have been evolving is like, feel the fear and do it anyway. Well, I want to tell the whole story. Basically, I am a developer advocate at Google. And then somebody, you know, the head of Web Dev Rail told me that, would you like to go to Latin America? And to do the roadshow, and I said, yes. Anything that I can do to help the region and the developers here to advance and to enable them to create awesome stuff, it actually makes me very, very happy. We've been in Sao Paulo and Rio, and now we're in Mexico City. We're doing one-day events and meeting fantastic developers. The web is a fantastic way to deliver content. You know, you can get content to users with very small downloads using progressive web apps. My life philosophy, my motto, I can have two motos on the web. The first is, keep it simple. The second is, focus on the user. The roadshow is a great way to educate people how to build fantastic user journeys from top to bottom in 2017 and beyond. Most of the barriers are self-inflicted. You really can do most of the things that people tell you not to do on your own. So keep pushing for what you want to achieve in life. For us, it's really interesting to actually get out there and understand the local culture as well. Just email or Twitter or something like that. It's not enough. You need to go out there and see there's actually human beings all over the place and understand both the opportunities they have but also the constraints. There's so many things in life that can be boring. When you actually get out there and you connect to people and you see all the similarities and the things you can do together, there's nothing better than that. Keep on exploring all the time. There's so many things that are terrifying and you don't want to take to risk, but it's almost always worth it. It's great to be back in Mexico City. I've met some really amazing developers here who are just building some really cool things. So it's been an awesome opportunity to connect with folks. Every day when I go into work or start any new project, I always think to myself, no obstacles, only challenges. My life motto is probably passion for your craft. So really believing in what you do and wanting to be the best at it. I can't wait to see what comes out of this, what gets built. This is exactly why we love doing what we do so much to see these sorts of events and these sort of passionate developers and visit these sorts of amazing cities too. We hope to come back, visit more countries and we really want to hear what you're doing, the PWAs that you're making and get in touch if you have something that you think we should look at. It's the morning of day two at Google I.O. 2017. Hi, I'm Timothy Jordan. I'm your friendly developer advocate on the ground here at Shoreline and I'm gonna be taking you around all the sandbox areas just like I was yesterday so that you can have some of the experience as if you were here with me. So let's go play around in the accessibility tent, which is of course all about giving all of your users a great experience with your service. So check it out. Hi Rob, how you doing? Good, how are you? I'm doing great. So can you help me with the accessibility of my website? Absolutely, so what we're showing here is the new Audits 2.0 panel in DevTools. So Audits 2.0 is basically taking the very popular Lighthouse Chrome extension and integrating it directly into DevTools. So this will run a number of checks on your site for rest of web app stuff, performance, best practices and especially accessibility, so. All right, well let's see what's wrong with it. Yeah, so here we've got a page that has some form elements which are missing their associated labels. And so if you want you can actually go and like click on these nodes right here and that'll actually take you into the Elements panel. So here we're showing the new Accessibility tab as well so you can inspect all sorts of information about this node, this little input field right here. That's really cool. Yeah, but it looks like it's not gonna read anything. Right, so right now we don't have a label for this control so a screen reader is not gonna know what to announce. So we actually do have a label element right down here. If you want you can control click on that. Yep, and add an attribute. You can type in four equals, sub dash name. Yep, hit enter. And what that does, now if you click on this input field again, there you go. Now you can see that you've associated the label with the control, it's got a nice name and a screen reader will announce it and tell the user what they're about to interact with. That's awesome. That's easy Rob, thanks. Yeah, absolutely. All right, well we're gonna go look at some more stuff. Thank you so much. Absolutely, happy to help. All right, let's go check out the selfie stick with my friend Austin. How you doing Austin? Good. So you've got a selfie stick which is designed for people that selfie sticks usually aren't well designed for. Exactly, yeah. So we have a pixel here running TalkBack which is a native screen reader to Android and we have a feature that will give feedback when it's in the camera mode to what's in the screen. Let me see what it says. Camera, two faces, shutter button, two faces, double tap to activate, two shutter button is now enabled, photo taken, two faces. Just took a selfie so it's giving audible feedback to what's in the screen if the face is centered and if it's good for selfie. All right, there's one more thing that I wanna check out. It's really cool and it's right over here. Hi Jessica. So this is a spoon for people who have say limited hand movement, right? And I'm just gonna go ahead and try and use it if that's all right. It's the trick is to try and dump the beans out. I can't do this. That's really cool. Thank you so much. I'll put that down. Hi everyone. My name is Ruby Paniker. I'm an engineer working on Chrome. And I'm Philip Walden. I'm an engineer working on the web platform team. Over the last year, we've been part of our metrics team in the web platform developing a set of new metrics and APIs that are user-centric. In that they capture user-perceived performance. We've developed a framework for thinking about user-perceived performance that we wanna share with you today. And Phil and I are really excited to be here sharing these metrics and APIs with you. In our past lives, we've been web developers and we understand the pains from gaps in real-world measurement. And before Google, I worked on web frameworks for apps like search, photos, G Plus, et cetera. And before working on the web platform team, I worked on Google Analytics. So I know a lot about, and I've seen a lot of the challenges around tracking performance in the browser. So this is the goal of our talk today. To help you answer this question, how fast is my web app? You certainly asked yourself this. And this may seem like a straightforward question, but the problem is that performance and fast, these are vague words. What does fast mean? In what context? Fast means different things on navigation or clicking, scrolling or animations. So what is performance and what is fast in these contexts and fast for whom exactly? Right, the truth is performance is hard. We kind of all know this. And for web developers, it's harder than it should be. That's one of the reason we're talking about this. There's a lot of tips and tricks that you might have heard and would not implement it or understood in the right context. They can sometimes make things worse. So in this talk, we don't want to give you more of these tips and tricks. We want to talk about a way to think about performance, a framework, a mental model for understanding performance measurement. And then the hope is that once you understand this model, you have a lot more tools at your disposal to solve performance problems yourself and your own app. But before we do that, let's talk about some myths and misconceptions around performance today. So I would say this is probably the most common myth that I hear, some variation of this sentence. I tested my app and it loads in x.xx seconds. So the reality is that your app's load time is not a single number. It's the collection of all the load times from every individual user. And the only way to fully represent that is with the distribution, like the histogram you see here. In this chart, the numbers along the x-axis show load times and the height of the bars on the y-axis show the relative number of users who experienced the load in that particular bucket. As you can see, while the largest buckets and the most users were between maybe one and two seconds, there were many, many users who experienced much longer load times. And it's important to not forget about these users. So this pattern toward the right is often called the long tail. And unfortunately, it's very common in the real world. And this histogram actually illustrates the difference between measuring performance in two very different contexts. And these contexts are measurement in the lab versus measurement in the real world. And by lab, I mean great tools like DevTools, Lighthouse, web page tests, other continuous integration environments you might have set up. Lab is important.   And they give you deep insight and breakdown so you can track down and fix problems. So lab is super important. It is necessary, but lab is not sufficient. Real world measurement on the other hand is messy. Real devices, various network configurations, cache conditions, all of these different conditions for real users are impossible to simulate in the lab. Real user measurement helps you understand what really matters to your users. It helps capture their actual behavior, which may be different from your assumptions or your lab settings. So to really answer the question of how fast is my app, it's important to measure this in the real world. So in our talk today, we will focus on real world measurement. So coming back to this myth for a second, there's another reason why the statement is problematic. The question, when exactly is load? Is an app loaded when the window load event fires? Does that event really actually correspond to when the user thinks the app is loaded? So I'd argue that load is not any one single metric. It's an entire experience. And so it can't be, sorry, I meant to say it's not one single moment. It's an entire experience and it can't be represented by just one metric. So to better understand and illustrate what I mean by that, I wanna show you an example. I'm gonna play a video of the YouTube web app loading on a simulated slow network. And I want you to pay attention to how the video loads, the app loads, and notice that things are kind of coming in one by one. So can we play the video? Okay, so think about how that felt.  Think about the experience. Can we play the second video? So it feels different, doesn't it? I bet some of you were not sure if the video was even playing. And that's kind of the point. When you don't give that feedback to the user, it makes them feel something. So these two videos, as I'm sure you guessed, load in the exact same amount of time, but the first one kind of seems faster. At least it feels nicer because things come out right away. It's like if you went to a restaurant and you sat down at a table, waited for an hour, and then they brought you your drinks, appetizers, entree, dessert, check, and dinner mint all at the same time. Like that would kind of feel weird. You would wonder why they waited until the very end. So again, you might look at this and then you might think, okay, well, we should optimize for the first initial render. Get content there as soon as possible. That's what this proved, right? And again, sometimes that's true, but that's not always true. Sometimes when you do that, you can make things worse in some cases. And cause other problems. So I'm gonna play another example, real life example from Airbnb's mobile website. And so for context, I know personally that the Airbnb engineering team cares deeply about performance and user experience and they try to make their pages as fast as possible. So one way they do this is use server side rendering to deliver all the content in the initial request. And it shows because the page loads really fast even on a slow connection. The problem is that on slower devices that take longer to execute JavaScript, the page is rendered, but it's not usable for a couple of seconds. And you can see that in the videos. Can we play the third video? So as you can see, the user here tried to click a few times in the search bar and then nothing was happening. And it wasn't until maybe the sixth click or so that the component pane from the top scroll down. And so to be clear, this video was from a simulated slow device. It doesn't represent the majority of their users, but Airbnb is committed to providing a good experience for all of the users and they wanted to fix this and they care about this and so they're currently working on a fix this problem. And I kind of just want to mention on a personal note that I'm really happy and glad that Airbnb was willing to let us show this to you. I think it's cool that they want other developers to learn from their experience. So can we go back to the slides? All of these examples that I just showed illustrate why you shouldn't measure load with just one single metric. Load is an experience and you need multiple metrics to even begin to capture it. So this is another commonly held misconception. You only need to care about performance at load time. Now loading is super important, but it's certainly not everything. And historically, we've all fallen into this trap of narrowly focusing on load. And part of it is just our own developer outreach. Our tools focus pretty much exclusively on loading. The reality is that there's lots of other interactions that happen long after load. All kinds of clicks, taps, swipes, scrolls. Think of all the time you spend on new sites in your email on Twitter or Amazon. Load is a really small fraction of this overall user session and users associate performance with their entire experience. And unfortunately, the worst experience stick with them the most. So this is a summary of the problems that we've highlighted today so far. Real world metrics are a distribution. They should be seen on a histogram, not as an individual number. Load is an experience. It cannot be captured with a single moment or a single metric. Third, interactivity is a crucial part of load, but it's often neglected. And finally, responsiveness is always important to users way beyond load time. So these are the questions that we want you to ask us today. And these are the questions that we hope we can answer for you as part of this talk. User-perceived performance is important. What are the metrics that accurately reflect this? How can we measure these metrics on real users? How can we interpret these measurements to understand how well our app is doing? And finally, how to optimize and prevent regressions going forward? So in this segment of the talk, we want to talk about these new metrics and the basic concepts underlying them.  The problem is that they don't really correspond to the user's experience of load. They have almost nothing to do with when the user saw pixels on the screen. For example, a CSS style might be hiding the content when DOM content load fires. And even if the content is rendered, interaction can be blocked. The JavaScript might not be there to hook up a critical handler, for example. And these old metrics completely ignore interaction, even though we know that interaction is super important for modern web apps. So what are the key experiences that matter to users and shape their perception? I think it's helpful to frame these as questions that the user might be asking. Is it happening? So did the navigation start successfully? Has the server responded? Is there anything that indicates to the user that it's working? And then is it useful? Has enough content rendered that the user can actually engage with the page? And once content has rendered, is the content usable? Like can they interact with it? Is it blocked? Is something preventing that interaction from happening? And finally, is it delightful? Are the interactions smooth, natural, free of lag or jank? And is the overall experience good? So now let's look at how these questions map to measurable metrics. Here's an illustration of a page's load progress. So the first frame over there is just the blank white screen before the browser has loaded anything. The second frame represents the first paint metric. It's the point at which anything is painted to the screen that the user can see, anything different from what the screen looked like before the response. The second frame shows first contentful paint, the second metric. It's when any of the content is painted. And by content, I mean kind of something in the DOM. It doesn't just have to be text. It could be images, or Canvas, or SVG, something in the DOM that's painted to the screen. In the third, or I should say, the fourth frame, you see some more stuff coming in, but it's not quite enough content to be meaningful. And then you get to first meaningful paint in the fifth frame, where the user can actually engage with the content. Enough stuff is rendered that the user can, what they came for is here, and they can start consuming it. And then finally, the last metric, timed interactive, is when the page is both meaningfully rendered and usable, meaning it's capable of receiving input and responding in a reasonable amount of time. So Phil said that the first meaningful paint is when the page is useful, and the user can engage. This is when the primary content of the page has rendered. But what is primary content? Which elements exactly? Now, not all elements on the page are equal. There are some elements that are important. We call them hero elements. And when these hero elements are rendered, you have arrived at the user moment of it is useful, and the user can meaningfully engage with the page. So here are some examples to show you what I'm talking about. These are hero elements for some popular sites. So for YouTube, we think on the YouTube Watch page, the hero element is likely the thumbnail of the primary video and the play button. For Twitter, it is likely the notifications count and that first tweet. For the weather app is probably the primary weather content, even though there might be tons of other stuff on the page. So when these hero elements have rendered, this corresponds with first meaningful paint, and the it is useful user moment. And you might notice that some of these hero elements are content-based, and some of them are more interactive components. Like in YouTube, for example, the hero element is rendered when the thumbnail is loaded and the play button is visible, but it's probably not actually usable until the JavaScript that controls the play button has run and enough of the video has buffered to actually be able to start playing. If hero elements are interactive, then not only does rendering them matter, but also when it's usable, when it's CDI is. However, there are times, as we mentioned, when interactivity can be blocked. So to understand why important elements might be blocked and not interactive, think about a time when you were in a long line somewhere, let's say the grocery checkout or the bank, you're standing in line and there's one or two customers who are confused or they're angry, and they hold up the line causing a long delay. This is what long tasks do on the browser's main thread. These are tasks that run long. They occupy the main thread for a long time, and they basically block all the other tasks in the queue behind them. And scripts are the most common cause of long tasks, like all the work that scripts do in terms of parsing, compilation, eval-ing, et cetera. So if you've used DevTools, you're familiar with all the primary type of work, style, layout, paint, script. It turns out all of this happens on the main thread, and it also so happens that most interactions, things like taps, clicks, and even animations typically also need the main thread. So you can see how this can be a problem. A long script is running, begging the main thread, and the user is trying to interact, and these interactions are basically waiting in the queue. And this manifests as jank to users as delays and click jank in scrolling or jank in animation. So you might wonder how long is long, what is long? And so we define long to be 50 milliseconds. Scripts should be broken into small enough chunks so that even if the browser is idle and a user happens to interact, the browser should be able to finish what it's doing and service those inputs, that interaction. And so 50 millisecond chunks will ensure that the rail guidelines for responsiveness is always met. Now you might have heard a lot about 60-fips and 16 milliseconds, and some of you might wonder why isn't this 16 milliseconds? And so the reason is, yes, if you are animating, then 16 milliseconds is important. But animation issues are a small subset of responsiveness issues at large on the web today. And if you know you are animating, then yes, you have to share the 16 milliseconds budget with the browser. Now long tasks are the cause of most of the responsiveness issues on the web today, and scripts are by far the most common cause of long tasks. So just to recap, this table shows how each of these metrics map to the user question from before. So the question, is it happening, maps to the metrics for first paint, first contentful paint? Is it useful maps to the first meaningful paint and the hero element timings? Is it usable maps to time to interactive? And then the last one, is it delightful maps to what should we just mention long tasks or maybe more accurately, the absence of long tasks? So you might be wondering how metrics like first meaningful paint or time to interactive can work for every app. And you're totally right. One size cannot fit all. We actually spend a lot of time in our metrics team trying to develop these generic standardized metrics that work for every app. And what we've learned is that it's incredibly hard to do that. And that also makes it hard to standardize. That said, there is value in these generic standardized metrics. And so these baseline metrics that work for the majority case, let's say 70% to 80% of apps out there. And we have made such metrics available in our tools. Like you might see them in Lighthouse, DevTools, web page test. And we are working to consolidate these definitions. Down the road, we expect analytics to start surfacing variants of these metrics. The main thing to understand for these out-of-the-box generic metrics is that don't assume that they accurately capture the use is it useful and is it usable moments for your apps. Try them out. See how well they work for you. And when it comes to real user measurement, we encourage you to supplement these metrics with your own custom user metrics or customize these metrics and make them your own. Make sure that they work really well for your app. And we'll show you specific tips for doing that later. So now that we understand and have these metrics, the question is, how do we get these in JavaScript? That's the most important thing to measure on real users. Historically, we've used, like we said, metrics like DOM content loaded and window load, primarily because they were easy to get in JavaScript. I assume every web developer here knows how to find out when window load happens or when DOM content loaded happens. But these other metrics have traditionally been a lot harder, sometimes impossible, to get in JavaScript. And trying sometimes to find them can lead to problems. This code sample shows how you would detect long tasks kind of before these new metrics. And this is kind of a hack. So what this code is doing is it's effectively making a request animation frame loop. It's doing measuring frame after frame after frame. And it's comparing the timestamps from the current frame to the timestamp on the previous frame. And if this is longer than 50 milliseconds, then it's considering it to be a long frame. But there's a lot of problems with this method. I mean, it kind of works, but it adds a lot of overhead. It prevents idle blocks. It's not great for battery life. And it doesn't even tell you the source of the problem. You might know that there was a long frame, and so you can assume there was a long task, but you don't know what script caused that long task. And this isn't just a hypothetical example. This pull request on the AMP project is basically them taking that code out because they realized that it was more trouble than it was worth. The number one rule of performance measurement code is that you shouldn't be making your performance worse by trying to figure out how good the performance is. So these hacks show the need for real APIs built into the browser so the browser can tell us when performance is bad. So for web performance APIs on the browser solution to real world measurement, these are standardized APIs. So they're available in multiple browsers, not just Chrome. And when available, we definitely recommend that you use these APIs. In practice, though, you will use a combination of these APIs as well as your own JavaScript polyfills. And the reason why polyfills are necessary is because the implementation timeline on browsers will vary. And we are asking you to customize and supplement these metrics. So these are kind of the core building blocks as we see it for web performance. We have high resolution time, which you might be familiar with, from your use of performance.now. Performance Observer is an important piece. It replaces the old performance timeline. And it overcomes its limitations, such as no polling. It's a low overhead API. And it avoids race conditions from a shared buffer. So this is kind of what the usage of Performance Observer looks like. And it also happens to be the code that replaces the hack that Phil showed you just a little bit earlier. So Performance Observer usage is fairly straightforward. You create a Performance Observer and make a callback. And then you say observe with expressing interest in certain entry types. And as entries of that type become available, the callback is invoked asynchronously. And there are many different entry types. Long tasks is what we show in this example. But this could just as well have been resource timing or navigation timing or paint timing, which is a new metric we've introduced. This also serves as a really good example of long task usage. You can basically use this code to understand responsiveness issues. This also serves as a really good example of long task usage. You can basically use this code to understand responsiveness issues on your app. The callback is called asynchronously when the main thread is observed to be busy for more than 50 milliseconds at a time. And long task is available in Chrome stable today. So I encourage you to try it out. So this table shows what our recommendation is for how you would track these metrics in your applications. And just to reiterate, having these tracked in your applications is what allows you to measure these metrics on your real users, not just running it in the lab. So first paint and first contentful paint can be measured with performance observer with the paint entry type. This is available in Chrome Canary today. Long task can be measured with performance observer also since Chrome 58. That's Chrome stable right now. For hero elements, it's a little bit trickier because you have to identify what your hero elements are. And you basically have to write some code to figure out when that's visible. And I should mention that along with this talk, I'm going to be publishing an article on developers.google.com very soon. It'll be up when this video goes up. That goes into more detail on how to do all of these things. So you don't have to worry about if you're taking notes or whatever. Also, I should mention that we're working on a native API to make this easier, where you can annotate, tell the browser, what are the hero elements, and the browser would tell you when they're loaded or when they're rendered. For first meaningful paint, at this point, before we develop a standardized metric, we think that you should use hero element timing as a substitute for first meaningful paint. The first meaningful paint metric is very, like we said, generic. It will try to be one-size-fits-all. Hero elements is for your site, and so it will always be more accurate than first meaningful paint. And finally, TTI, we released a polyfill today, actually. For the TTI polyfill, it's on GitHub, and you can go try it out right now. To give an example of what the usage looks like, you essentially import the module in JavaScript, and then you call the get-first-consistently-interactive method, and that returns a promise, and the promise resolves to the TTI metric value in milliseconds, and then once you have that, you can send it to Analytics. Did you get a sense for what the polyfill does? I should mention that the get-first-consistently-interactive method takes an options object, so you can figure it for your site. And what you can do is you can pass it a lower bound. The polyfill will assume the lower bound by default is DOM content-loaded, but you can give it a better metric for your site. So the way this works is you have the main thread with long tasks and short tasks, and you have the network timeline, and then you have your lower bound, which by default is DOM content-loaded. But the polyfill does is it uses these resource timing and long task entries to search forward in time for a quiet window of five seconds, at least five seconds, where there are no long tasks and no more than two network requests. Basically, it's saying, once we get to that quiet window, we think that the app is most likely interactive now, and then it considers the moment of interactivity to be where the last long task was. So that's a bit of how this polyfill works. Again, you can pass it a custom lower bound for your site, and one example of what you might want to use is the hero element timing. That would be a great example. You also might want to pass, basically, the moment all of your event handlers are added, because if your event handlers have not been added yet, the site is probably not interactive yet. So Phil showed you how long tasks can push out your time to interactive. But there's lots of other interactions that we're asking you to care about, maybe on learning, like plicks and taps. And delays in these can basically cause pretty bad user experiences. So you probably wanted to know when these important events are delayed. And ideally, there would be a first class platform API that would answer this question. And we actually are working on such an API. But today, you can actually use this code sample to understand the gap. You can basically use the difference of event.timestamp and the current time in your event handler. Now event.timestamp is our best guess of when the event was created. And so this can be the hardware timestamp, or when our best guess is when the tap, you tap the screen. And this difference will tell you how long the event was spending waiting around on the queue for the main thread. Now here, if that difference is more than 100 milliseconds, we send it to analytics. Now we haven't shown this here, but you can also correlate this back to your long task observer. Like you can actually look at what long task happened in this time when my event was blocked waiting. And those are likely the culprits. So once you've measured these key metrics and sent them to some analytics service, you want to report on them to see how you're doing. That will allow you to better answer the question, is your app fast? So this is just one example of a histogram that I threw together from TTI data for an app that I maintain using the polyfill that we just showed you. And the point is not to look at these numbers or compare them, but the main point that I want to make is when you're tracking your performance metrics in your analytics tool, then you can drill down by any dimension that your analytics tool provides. So in this case, we can see the difference between performance on desktop versus mobile. You might also want to consider the difference between one country from another country or geographic locations where maybe network availability is not as great or network speeds are not as high. It's important to know how those difference manifests across in the real world on real users. In cases where you can't show a whole histogram, I recommend using percentile data. So you can show the 50% the median number. You can also show things like the 75th percentile, the 90th percentile. These numbers give a much better indication of what the distribution was. And they're much better than just averages or just one single value. So a really important question is, do performance metrics correlate with business metrics? And again, if you're tracking your business metrics in an analytics tool and your performance metrics in an analytics tool, and this shows the value of tracking the stuff on real users, then you can see and you can answer this question. All the research that we've done at Google suggests that good performance is good for business. But the really important thing is, is this true for your users, for your application? So some example questions you might want to know. Do users who have faster interactivity times buy more stuff? Do users who experience more long tasks during the checkout flow drop off at higher rates? Like these are important questions, and once you know the answers to these questions, you can then make the business case for investing in performance. I hear a lot of developers saying they want to invest in performance, but somebody at the company won't let them or won't prioritize it. This is how you can make that a priority. And finally, we haven't talked about this yet, but you may have been wondering, all of the data we've been showing is for real users who made it to interactivity. And you probably know some users don't make it there. Some users get frustrated with the slow load experience and they leave. And so it's important to also know when that happens, because if it happens 90% of the time, the data that you have will not be very accurate. And so you can't know what the TTI value would have been for one of those users, but you can measure how often this happens. And perhaps more importantly, you can measure how long they stayed before they left. So we've discussed a lot of specific metrics and APIs, and we've shown you code samples. And so now we kind of want to back up a little bit and provide some higher level guidance on how to best leverage these metrics and APIs. So one great thing about everything we've introduced today is that all of these are user-centric metrics and APIs. So by definition, improving these will improve your user's experience. So the first piece of wisdom is drive down first paint and first contentful paint. And all of the traditional wisdom for fast loads applies here. Remove those render blocking scripts from head. Identify the minimum set of styles you need and inline them in head. You might have heard of the app shell pattern that helps improve user-perceived perception. The idea there is very quickly render the header and any sidebars. Now, first paint and first contentful paint are important, but they are certainly not sufficient. It's really important to improve your overall load time. So it's not just enough to be off to a good start in a race. It's really important to make it past that finish line. And time to interactive is the finish line for loading for interactive apps. So more specifically, minimize the time between first meaningful paint and time to interactive. We saw in the Airbnb demo it was important for users to interact with that search box. Now, to shorten your time to interactive, identify what is the primary interaction for your users. Don't make assumptions here. Do they tend to browse or do they tend to interact with a certain element right away? And then figure out what is the critical JavaScript that's needed to power that interaction and make the JavaScript available right away. One common culprit we've seen are large monolithic JavaScript bundles. So splitting up JS, like code splitting, will take you a long way there here. And the purple pattern kind of fits in here, specifically the first, the P and R of purple. Ideally, ship less JavaScript. But if not, at least defer the JavaScript. There's tons of JavaScript that the user is never going to need, all those pages that they're not going to visit, all the features that they're not going to interact with. If there is a widget in the footer that's below the fold that they're unlikely to interact with, defer all of that JavaScript. Third thing we have is reduce long tasks. Cracking down on the long tasks will really help responsiveness on your app overall. However, if you really need to prioritize, at least think about long tasks in the way of those really critical interactions. On load, it's long tasks that are pushing out time to interactive, or long tasks that are in the way of the checkout flow or other important interactions for your app. Scripts are by far the biggest culprits here. So breaking up scripts will certainly help. And it's not just about breaking up scripts on initial load. Scripts that load on single-page app navigations, like going from the Facebook home page or the profile page, or clicking around, like on the checkout for Amazon or the compose button in Gmail, all of this JavaScript needs to be broken up so it doesn't cause responsiveness issues. And the final thing we have for you today is holding third parties accountable. Ads and social widgets are known to cause the majority of long tasks. And they can undermine all of your hard work on performance. Like you might have done a ton of work to split out all your code carefully, but then you embed a social plugin or an ad, and they undo all of that work. They get in the way of critical interactions. So to get an idea of this, we're actually doing a partnership with Soasta, a major analytics company.  They picked a couple of their sites, their customers, who had third party content. And on the first site, they found that 93% of long tasks were because of ads. On the second site, they found 62% of long tasks were about evenly split between ads and social widgets. Now, long tasks API actually gives you enough attribution to implicate these third party iframes. So we encourage you to use the long tasks API, find out what damage these third parties are doing on your apps.  You don't want to put a bunch of work into this and then have it all be for nothing if one new release turns everything bad. And so it's critical that you have a plan for preventing regression. So this is a workflow that I promote. You start off with writing code. You implement a feature, fix a bug, improve the user experience in some way. And then before you release it, you test it in the lab. I assume lots of people do this.  Make sure that it's not slower than your previous release. And then once you release it to your users, you also are going to want to validate that it is fast for those users that you release it to. You can't just test in one. You should these things complement each other. You should be testing both in the lab and in the real world. And so for some automation ideas, the best way to prevent regression is to automate this process. You're probably going to slack on it a little bit if you don't have it built into the release and automated. So Lighthouse runs on CI. And there's actually a talk tomorrow afternoon by Eric Bidelman and Brendan Kinney that kind of goes into how to do this. And I recommend checking that out if you want to learn how to run Lighthouse on CI. If you're using Google Analytics, you can set up custom alerts that trigger when some condition is met. So for example, you could get an alert if suddenly the number of long tasks per user spikes, maybe a third party you were using changed their JavaScript file, and things got worse and you didn't know. And so this is a good way of finding out that stuff. So getting back to the original question, how fast is your web app? In this talk, I hope we've given you enough of a framework to think about performance in a big picture in a user-centric way. I also hope we've given you enough specific tools, metrics, and APIs that you need to answer this question for yourself. We know the situation isn't perfect. We know we have more work to do, and Shuby's working on this, leading her efforts here at Google on the standard side, and so she can talk about some of the things that are coming down the road. And so this is our final and last slide, and I just want to say that yes, we know there are gaps and there's a number of APIs that we're working on. We'd love to have a first-class API for hero element timing. The idea there is that you guys can annotate the elements that matter most for your sites and then the browser can put those times on the performance timeline. Secondly, we are working on improving long tasks, mostly by improving attribution. We really want to tell you which scripts are causing problems and more detailed breakdowns, so you can actually take action right away and fix those issues. Secondly, we want to really have an API for input latency, so you don't have to go through all those workarounds that we showed you for MN timestamp. Ideally, for your important interactions for your app, you should be able to know, like, how delayed they were, which long tasks were in the way, and when the next render happens. And then there's other inputs that we haven't even touched on that are in our backlog, things like scrolling and composited animations. And finally, I just want to leave this with saying, you know, we've said a lot today, but we really want this to be a two-way dialogue. We want to hear from you. We want to hear about your frustrations.  Try out these APIs and polyfills, and please file bugs on the spec repos on GitHub. This is actually the best way to report issues and make feature requests. And if you're working with analytics, like whether it's a different team or a third party, push on your analytics to adopt these new metrics. Ask them for these histograms, like Phil showed you, and we are pushing on analytics to an R end. Start the chromium bugs on performance. This is actually a signal we use for prioritization internally, and we need these signals to make a case for working on measurement. And finally, as Phil said, we have all the links in the article that he will publish shortly, and they will also be linked from the video. Yes, so thank you, and this is how you can get ahold of us, and these mailing lists are how you can submit feedback. Hey, everyone, Sam here. In this web series, we'll solve common web problems with standards. These techniques are part of the web platform and work with any framework or library. Okay, let's go. If you're animating some elements, whether you're using CSS, the web animations API, or libraries which just use request animation frame, make sure you help your browser stay speedy by letting it use your 3D graphics card. Sam, what are you talking about? When you look at a website, it's made up of layers stacked on top of each other, and your graphics card won't need to redraw for changes to only some certain CSS properties, specifically opacity and transform. These two properties let you move stuff around and change how visible it is. You can animate, you know, width, color, or other CSS properties, but do it sparingly and not in response to user action, where you want to be snappy. To keep things fast, if you have elements that move around a lot, you also want to set the will change transform or opacity CSS property. This is a hint to the browser. This element should be a layer all the time. Otherwise, when you upgrade or downgrade to a layer, you'll incur a cost. The browser has to redraw that specific part of the page. But if you give everything in your page a layer, this might bog down your browser as it struggles to compose them all together. This is a complicated topic, and you should check out more documentation to learn more about it. And most importantly, your site doesn't need to be perfect. It won't crush your user's experience to make a few mistakes here and there, but it's good to keep on top of it. Check Chrome's rendering section inside developer tools once in a while just to see what's happening and how your site's going. This was layers the standard way. See you on the next tip. The only thing that evolves faster than technology is our expectations. We want everything better, easier, now. Suddenly downloading an app feels like it takes forever. And in many parts of the world, data is still at a premium, with one megabyte costing up to 5% of a monthly wage. Let's face it, though. Until now, the alternative to native apps hasn't been great. Progressive web apps can now deliver mobile web experiences with a native-looking feel, offering features like real-time push notifications, adding a site to your home screen so you can easily jump back to it with a single tap, even when you're offline, plus the ability to make quick payments on the go, all from your browser. This is the next generation of the mobile web. So what are we waiting for? Let's go and build something great. I'm Wojtek Kalciński. This is Android Tooltime. And let's talk a bit about the Espresso test recorder and how it can help with adding UI tests to your app. But first, a short explanation for those unfamiliar with Espresso. Espresso is a testing framework designed to provide a fluent API for writing concise and reliable UI tests. However, it is often the case that developers are reluctant to add UI tests to their apps or simply don't have time to learn the framework. This is where the Espresso test recorder comes in. It lets you create and add UI tests to an existing app in an interactive way. You may have previously seen the beta version of this feature, but in Android Studio 2.3, we're promoting it to stable with a few enhancements. To get started with the test recorder, click on Record Espresso Test under the Run menu. The device selection dialog pops up, and after you make your choice, the test recorder runs your app in debug mode. Simply progress through your app's UI as a regular user would by clicking buttons, swiping and typing into input fields, and all those actions will appear in the test recorder window. You can also click here to add an assertion to your test at any time during recording, which will trigger the test recorder to dump the current view hierarchy. To select the view you want to assert on, click on the screenshot that appears in the recorder window and choose between the assertion type from View Exists, Doesn't Exist, or check that it contains the specified text. When you've finished recording your test, the test recorder generates the equivalent test code to run your actions and assertions and puts it in a new file in your Projects Instrumentation test folder. It also checks if your build file contains the required Espresso dependencies and adds those if needed. When you look at the source file that Espresso test recorder created, you will see that it's perfectly normal human readable code. So if you need to further customize your tests or alter them when your app changes, you can simply open the file again and make the alterations you need. As you can see, the Espresso test recorder is very simple to use, but it does come with some limitations. As of Android Studio 2.3, only a few most common assertions are available through the recorder UI. So if you need anything more complicated than that, you will need to edit the generated code by hand. Also, at this stage, the test recorder cannot handle all situations where additional synchronization is needed to deal with delays and async operations in your apps. I highly recommend getting familiar with the Espresso idling resource API and using that in your tests to signal when a long running operation happens. For advanced users who want to tweak some aspects of test code generation, there's a settings page for the test recorder in Android Studio Preferences. Here you can change the maximum view hierarchy depth that will be used for view identification and if app data should be cleared every time you record a new test. The Espresso test recorder is a great way to start adding tests to your app, whether you want to learn Espresso by examining the generated code or simply to quickly build a test suite which you can customize later. We look forward to your feedback on our social channels and happy testing. Consider the simple URL. A few years ago, these were pretty straightforward. You clicked on one and nine times out of 10, you went to a web page. Then things changed. People started using their mobile devices for, well, everything and these devices in turn started supporting the idea of deep links. Click on one of these deep links and it could take you not just anywhere on the web but anywhere in an app as well. So you could use a deep link to point directly to a specific restaurant inside a reservation app or give your new customers a personalized welcome based on the link that brought them to your app in the first place. At least that's how they worked in theory. In practice, deep linking had issues. The same link wouldn't necessarily work on an iOS or Android device and they behaved very differently or didn't work at all for users who didn't have your app installed. And for people who didn't stall your app through a deep link, all of that great link info was typically lost during the installation process, leaving your personalized warm welcome out in the cold. So while deep links were great in theory, their uses were a little more limited in practice. Enter Firebase Dynamic Links. Firebase Dynamic Links are deep links that work the way you want them to. So you can create one single link that behaves one way on iOS, another on Android and even a third on a desktop browser and it will take you to a place that's appropriate to that platform. You can also set up dynamic links to change their behavior depending on whether or not your user has your app installed. For users who don't have your app installed, maybe you send them to your website, maybe you take them to the Play Store, or maybe you show them an interstitial describing the benefits of your app before you take them to the app store for a smoother transition. More importantly, these links can survive the app store installation process. So if your user installs your app when clicking on a dynamic link, all of that information is still available to you when your user opens up your app for the first time. So what does this mean? It means you can use Dynamic Links the way you've always wanted to use deep links. You can use them in marketing campaigns from email to social media to banner ads to heck, even QR codes. And in addition to install attribution tracking, you know, the kind that lets you know which campaigns are getting you the highest quality users, you can also give your users a customized first time experience based on the campaign that brought them there. So if a user installs your music app because you showed them an ad for classical music, you can make sure your app takes them right to show fans latest hits when they first open it up. Dynamic Links are great for sharing too. Your users can use them to share recipes, links to their favorite level in your game or even coupon codes. In fact, Dynamic Links are the technology that powers Firebase Invites. And because Dynamic Links are a Firebase product, you can see their stats directly through the Firebase console. Find out how many people clicked on a link or use Firebase Analytics to find out which of your users first opened your app through a particular link. To find out more about Dynamic Links, check out the documentation here and give them a try and deep link away. Okay, Google, what's the temperature like at Mount Everest? The temperature there is minus 14. Ooh, I better pack a jacket. Oh, hi, I'm Wayne Pekarski and today I'm gonna talk about the Google Assistant and how you can develop your own actions to be a part of this new ecosystem. At Google, we've been providing assistance to users for years across many of our products, but we think there's much more we can do to help people get things done right when they need it in a conversational way. And that's why we're building the Google Assistant. The Google Assistant can help users get things done throughout their day, whether they're at home or on the go. And it powers devices like, for example, the Google Home, a voice-activated speaker. To better serve user requests, the Google Assistant needs to work well with an ecosystem of everyone's favorite services. Actions on Google allows you as a developer to integrate your services with the Google Assistant. And that is what we're gonna explain how to do in this video. Conversation actions enable you to fulfill a user's request directly via a two-way dialogue. Users don't need to pre-enable skills or install new apps to interact with any actions you build. When a user asks for your action by name, we'll connect them with you immediately. Let's first go through a detailed example of a user interacting with a conversation action. Think about something as simple as helping a user choose what to have for dinner based on their mood and the ingredients they have around. Let's call this action personal chef. The user first needs to invoke your action with something like, okay, Google, let me talk to personal chef. The Assistant will then introduce your action and now the user is talking to you directly. From this point onwards, you get to interact with the user and have a conversation. Okay, Google, let me talk to personal chef. Sure, here's personal chef. Hi, I'm your personal chef. What are you in the mood for? Well, it's kind of cold outside, so I'd like something to warm me up, like a hot soup, and I want it fast. All right, what protein would you like to use? I have some chicken and also some canned tomatoes. Okay, well, I think you should try the chicken tomato soup recipe that I found on example.com. Hmm, sounds good to me. So this is a pretty rich interaction. Think about all the sentences I spoke and how the action needs to extract the meaning out of this. How would you implement this? If you're an expert in the area of natural language processing, you can use the Conversation API, which allows you to process the raw strings that contain the spoken text from the user. You can then use the Actions SDK that includes all the tools and libraries you need to build the actions. However, if you don't want to process the user's transcribed speech yourself, you can use one of the tools that have integrated with Actions on Google. One of these tools is API.ai, which provides an intuitive graphical user interface to create conversational interfaces, and it does the heavy lifting in terms of managing conversational state and filling out slots and forms. This means you'll no longer need to process the raw strings. API.ai can do this for you. To handle a conversation, you use the API.ai developer console to create an intent. This is where you define the information you need from the user. For our example, finding a kitchen recipe, this would be the type of food, the ingredients, the temperature, and the cooking time. You then specify example sentences. API.ai parses these sentences and uses them to train its machine learning algorithm to process other possible sentences from your users. You don't have to write regular expressions or a parser. You can also manually set what the acceptable values are for each piece of information. Once this is done, API.ai uses these definitions to extract meaning out of spoken sentences. The user can provide information naturally, out of order, all at once, or in pieces. The action can ask follow-up questions as needed. Pretty neat, right? Once you've set up everything in the API.ai console, you can then test it out immediately with example sentences. Then you can test your project with the web simulator, preview it on Google Home, or deploy the full project to Google, all from within API.ai. Next, you can connect up in an optional web hook to your intent to allow it to interact with a backend server. When all the details you need are filled in, your web hook is called with the appropriate details provided as JSON data. You don't need to worry about parsing strings or dealing with responding back with follow-up questions for the user. You can also develop the web hook using the language and hosting platform of your choice. It's just an HTTP callback. So API.ai makes this really simple. It's easy to get started and you can have a prototype working in just a few minutes. You should check out our screencast video where we show all the steps to make this happen. So the Google Assistant is the next big opportunity for developers. By developing actions on Google, you'll get cutting-edge experience in natural conversation interfaces and be ready to actively participate in the emerging space of AI-first computing. In addition, you'll be able to help shape the platform and grow your audience in all the devices and contexts where the Assistant will be available in the future. And thanks to conversational interface building tools like API.ai, as well as Google's unique understanding of the user's interests and context, you'll be able to create frictionless, intelligent experiences for people that engage with the Google Assistant. You can find out more about actions on Google by reading the documentation at developers.google.com slash actions. We also have an actions on Google developer community on Google Plus, so you can ask questions and share your ideas with everyone. We look forward to seeing what you build and I'll see you next time. Hey, everyone. So I actually have a sixth sense for donuts. I know where they are and I'm sensing they're right over there and all I have to do is use the Android Pay to get one. Shall we do it? Yes, we should. Hey, how you doing? Great, how about yourself? I'm doing fantastic. So what do I do here? Just tap your phone with the Android Pay loyalty card on it and we'll get you a donut. There it is. The green one, clearly. Thank you so much. Thanks, guys. Okay, so Dennis and I clearly have been using Android Pay a few more times. Dennis, tell me about what just happened there when I tapped my phone on. So we're doing actually tap for treats. To go to stations right now, you basically tap through NFC and basically it's a loyalty card that's set up that basically you can collect points off of every time you do a tap. So you go to the stations, tap. We'll give you a nice donut, some other treats. After you get four taps, you can come back to the station later on in the day and you can get a free Android bot. So this is something that people are doing all around the festival today? Correct. And I can see as I've been passing by throughout the day that it's also very, very popular and I don't think it's just the donuts and I think it's also pretty easy. Yeah, it's super easy to do. Literally take seconds to actually recognize through the NFC tap. You know, people obviously love Android bots as well so that's probably another big incentive of why people are doing it. Oh, and wait, there's more than just donuts, right? There is more than just donuts. You actually have a custom bot that you can get once you tap four times. Come to this terminal, people can actually get a nice free custom bot that's specifically Android Pay on IO 2017. That's really cool. Yeah, here you go. Oh, really? Thank you. All right, thanks. All right, my hands are full. I'm gonna take care of this and we'll get to something else. Welcome. Thanks for joining us this afternoon. My name is Megan Lindsay and I'm the product manager for WebVR at Google. Today, I'm here to talk to you about WebVR. I'll show you the opportunities that opens up for web developers. How this is gonna benefit the VR ecosystem as a whole and what others are already doing with it. Then my colleague, Brandon Jones, will demonstrate how easy it is to build a cross-device WebVR app by doing it right here on stage. WebVR enables web developers to build fantastic cross-platform, cross-device VR experiences. Here's a quick overview of what WebVR is all about and an introduction to our recently released WebVR experiment site. VR should be accessible to everyone because it has the potential to let everyone explore, play, and create in amazing new ways. But right now, VR is pretty complicated.  And then when we wanna play with their awesome VR stuff, we've gotta have the right headset. VR should be easier so developers can make something quickly and share it with everyone, no matter what device they're on. Kind of like how easy it is to share stuff on the web, but with VR. Well, that's the idea behind WebVR. It's VR on the web for everyone. Here's how it works. Say you're in a browser like Chrome and you come across a WebVR experience. You just tap the link, put on a headset, and boom, you're in VR. Developers can build WebVR things the same way they build web things with JavaScript. And since it all works in a browser, it's easy to make it work for all kinds of VR devices, whether it's someone using their phone, their computer, or their entire room. Developers are already building and sharing awesome stuff with WebVR. We've started showcasing their work on a site called WebVR Experiments. It gives you a glimpse into the kind of stuff that's possible. You can play simple games, see the world in a new way, explore interactive stories, play with a friend, or lots of friends. Each experiment comes with open source code to help others make new experiments, and developers can submit what they make. All of this is an effort to make VR more accessible so anyone can build and everyone can play with awesome VR stuff. So come and start playing at webverexperiments.com. I wanna tell you why we at Google care about WebVR and why we're investing in it. As Clay Bavor said in the keynote yesterday, immersive computing is going to change how we play, work, live, and learn. We're at the start of the next computing revolution. Many of you have seen the technology adoption curve before. 2017 is shaping up to be a pivotal year for VR where it's moving beyond innovators to early adopters. This is a time of opportunity. However, one of the largest barriers to even broader VR adoption and more user engagement today is content. Content is absolutely critical to the success of any new ecosystem. Giving users great, diverse, and plentiful things to choose from will keep them coming back. I believe that the open web is exactly what virtual reality needs to take it to the next level, and WebVR is the first step along that path. WebVR opens up VR to the largest developer platform in the world. You, web developers, you can build for VR for the first time. The web's an open ecosystem that we at Google strongly believe in and support where developers from around the world work together to innovate in a standardized, interoperable way. The web isn't controlled by any one company and it's unique in providing access to content from any device through any web browser. There are no walled gardens here. What this means is that WebVR simultaneously decreases the barrier to entry and extends the reach of your content. Using WebVR, you can start developing for VR with gradual investments by progressively enhancing your existing websites. You can light up your site with VR when an immersive experience adds something special from breaking 360 news on the ground to exploring your next home. With WebVR, you can build your experience just once to reach all VR headsets and the billions of mobile and desktop users, giving you access to the broadest audience possible. And you gain all the benefits of the web by making your content searchable, linkable, and low friction with no installation required. Sharing is as easy as a link. So the potential of VR goes well beyond gaming. What kinds of VR content just makes sense to do with WebVR? Your imagination's the limit, though I believe the first wave of WebVR content will be the use cases that are already first and best on the web. Ephemeral content found primarily through search and social media, short form media content and important, but perhaps less frequent tasks where you may just not want to keep a nap around. So let's take a look of some of the things that others are already doing with WebVR. Matterport has created technology that allows capturing real world spaces in 3D to view them virtually for industries such as real estate, travel and hospitality, and architecture, engineering and construction. Matterport customers like Sotheby's Home Visit and Mansion Global have scanned nearly half a million places and make these available to their users with Matterport's web player. The web player lets the user navigate through the 3D virtual space on their phone or desktop. Before WebVR, users were required to download a separate app to view the full VR experience. This created a lot of friction and resulted in significant user drop-off. But now with WebVR, this friction has been eliminated. The user can step right into the home they are looking at directly from the website. And when the user exits VR, they're still on the original website rather than in a separate app. Matterport supports WebVR for Daydream View and Cardboard support is coming soon. With over a million scenes created and posted by their community, Sketchfab is the world's largest platform to publish, share and discover 3D content online. With WebVR, any Sketchfab model can be viewed and manipulated in your VR headset. Content creators or enthusiasts can use Sketchfab to share or embed models anywhere on the web, enabling them to be explored either on a 2D screen or in VR. Pouster creates custom experiences for movies and music, helping with discovery of major entertainment products. With the rise of virtual reality, Pouster used WebVR for the broadest audience reach and created experiences focused on movie websites, showtimes and ticketing. Here's a look at what Pouster has done recently. So, Movie Studios saw over five times more movie theaters selected inside VR than on the regular websites. Audiences viewed the 3D trailer and the 3D gallery images and they converted to seeing the movies in 3D rather than in 2D in the actual theaters. And finally, from filmmaker Christopher Nolan comes the epic action thriller Duncork opening worldwide this July. Here's a preview of what you'll see in Duncork. We'll fight on the beaches. We'll fight on the landing grounds. We'll fight in the fields and in the streets. We shall never surrender. G13, experience it on IMAX July 21st. Just as the film offers a first-person perspective, Warner Brothers wanted technology to offer a deep, immersive perspective on just what happened at Duncork. They brought this vision to life as one of the first collaborative VR experiences on the web, showing the depth of soldiers camaraderie through a cooperative experience between two people. Working together to survive the evacuation, each player will become both the rescuer and the victim. Here's a taste of what's coming for Experience Duncork. Experience Duncork will be releasing in June and will be open to everyone supporting 2D devices as well as VR headsets. A teaser of the experience is live today, so check it out. Other areas we're seeing particular interest in WebVR include news, e-commerce, interactive VR films, education, art, and custom business solutions. We are eager to see what web developers use WebVR for next. So WebVR content's arriving, and WebVR browser support is already here. In Chrome for Android, we've released WebVR support as an origin trial for Daydream View and Google Cardboard. Our friends at Mozilla, Microsoft, Oculus, and Samsung have all released or announced coming support for WebVR, bringing it to Samsung Gear VR, Windows Mixed Reality, Oculus Rift, and HTC Vive. In Chrome, we're continuing to improve and extend our WebVR support. In our latest release of Chrome for Android currently in beta, we've significantly improved performance, making it more consistent and stable overall, and making it easier to reach target frame rates by adjusting rendering settings. We've also released WebVR support for Chrome custom tabs, enabling you to enhance your native Android app with your WebVR content, too. Looking forward, we have support for desktop headsets in development. And we're bringing great WebVR content right to the Daydream home screen. Stay tuned for more on this soon. We've talked about progressive enhancement of VR content and how this is a superpower unique to WebVR. Let's dig a little deeper into what that actually means and how others have solved it. Weather.com recently released an interactive WebVR experience called The Birth of a Tornado. They applied progressive enhancement and responsive design principles to ensure their experience can be used on any device by optimizing the interaction model for the device being used. On desktop, you drag with your mouse to change the viewpoint and click to interact. On tablet, you change the viewpoint by dragging with your finger and tapping to interact. On mobile, the phone's accelerometer is used to provide a magic window into the VR experience. For Google Cardboard, head movement is used to gaze and target and tapping the button to select. And Daydream View uses the controller for interaction. Birth of a Tornado also works with Samsung Gear VR and the HTC Vive. The model used in Birth of a Tornado can be used with many WebVR experiences. And we have a library to help make this easy that Brandon will show you a bit later. This is just one way to support cross-device experiences. Another example is Dance Tonight. Some of you may have experienced the Dance Tonight project at I.O. yesterday evening, also built in WebVR. Dance Tonight is an ever-changing VR experience made by LCD sound system and their fans. It's made entirely from VR motion capture recordings of fans dancing to a new song by the band. Another special thing about the project is that it works across devices but playing to their individual strengths. On desktop and mobile, you get to be in the audience. On Daydream, you're on stage.  If you didn't catch this in person, it'll be available online this summer. Your input choices and supported devices may differ for your WebVR project, though we recommend starting with a goal of universal access as a best practice and just see how far you can go. While WebVR content is still best experienced immersively in a VR headset, most people have still never tried immersive VR at all. WebVR content will be their very first hint of what they're missing. The great WebVR content that you create will be the reason a new user decides to pick up their very first VR headset. I hope you're as excited about WebVR's potential as I am. Now I'd like to introduce Brandon Jones. Brandon started WebVR as his 20% project several years ago and co-authored the spec.  Please welcome Brandon. Thank you, Megan. So Megan talked about the principles of progressive enhancement. That is making pages that can be used on the desktop and mobile devices as well as across multiple VR devices. That can seem very intimidating, but the right tools can make it fairly simple. It all starts with creating some great WebGL content. WebGL is an API for rendering 3D graphics to the browser and is supported across all platforms today. There are many great WebGL tools and frameworks out there to help you bring your ideas to life. From there, turning your WebGL page into an immersive WebVR experience can be as easy as adding a few lines of code. To show you what we mean, we're going to build a quick WebVR experience on stage today. The app that we're going to be building will be a 360-degree photo viewer, which is a great fit for WebVR. These type of photos are easy to create with many cameras available that capture them and provide a fun experience that you don't get from traditional photos. Best of all, they can easily be viewed in 2D and browser with a click and drag or magic window controls, while VR can optionally be used to provide an enhanced viewing experience for users with the right hardware. 360-degree photos also represent a class of content that's difficult to get users to install a native app for. Because of the content simplicity, the overhead of an install is probably enough to discourage most users, given that they likely only expect to spend a few seconds looking at each image. It's very likely that most users would never get past the app store link. Ideally, they could fluidly step into VR and out of VR with very little overhead, view the images quickly, securely, and then move on without having to uninstall anything afterwards. This sort of ephemeral experience is what WebVR excels at. So now we're going to switch over to the code and actually build the experience. Now, we're starting out here. Can we get the laptop up on screen? Thank you. We're starting out here with some boilerplate code. We're using 3JS. It's not the only framework that you could use for creating WebVR and WebGL experiences, but it is a fairly common one. There's also frameworks that are expressly for WebVR content, such as A-Frame or React VR. But because 3JS has fairly wide developer acceptance already, we're using that today as our example. Now, the boilerplate that we're starting here with is fairly simple, so I'm not going to cover it in detail. This is the type of thing that you would see in a 3JS tutorial zero. And what it produces for us is a black screen. Well, that's OK. That's a great starting point. Thank you. That was awesome. So because we're creating a 360-photo gallery viewer, we need images. Now, normally, these would come from, of course, a database, a CMS of some sort, but we're just going to hard code them in for the sake of example. And then we need a way to view them. Now, because it's a 360 image, the method that we're going to use is to create a gigantic sphere, in this case, about 500 meters in radius. Invert it, and that's what the viewer's skill here is doing, is it inverting on the x-axis so that all of the faces point inward, and then keep the camera for a scene at the very center of that. That way, when you look around, you're seeing this sphere all around you that's practically at infinity. Next, we need to put our image on the inside of that sphere using the 3JS mesh basic material. This loads up what's known as a texture in WebGL and will apply it to the inside of that sphere. And it just so happens that 3JS's default coordinate systems work out really well for equirectangular images, which is the default that most 360 cameras spit out. Finally, we need to combine the geometry and the material together into a 3JS mesh, which is the basic primitive that it uses to render, and add it to the scene. So once we've done that, we can switch back to the browser and see that we now have a photo. Unfortunately, there's no interaction, so we can't tell that it's a 360 photo. We'll fix that by pulling in what's known as the WebVR polyfill. The WebVR polyfill is a JavaScript implementation of the WebVR API that's targeted primarily at mobile devices. It uses their accelerometers to provide basic head tracking used for a cardboard style experience. It also happens to provide us with a basic emulated click and drag mode on desktop that we can use to get basic functionality on this desktop computer. Now, in order to make our application responsive to the WebVR polyfill, we have to add a 3JS extension called VR controls. We'll attach this to the camera, and then this makes it so that any head motion that happens on your headset is automatically applied to the camera itself. In order to make sure that it keeps updating with the head motion, we also need to add an update function to the animation loop. Once those two elements are in place, we now have basic click and drag functionality, and we can see that we actually now have a complete photo viewer for a single photo. But that's not terribly interesting. We want a gallery. So the next thing that we're going to do is provide a 2D version of the thumbnail gallery that allows us to switch between images. We'll loop through the image gallery that we had loaded up previously, load a texture for each, and then pass them to this add to gallery 2D function that we're about to define. Here we're going to use a little bit of basic HTML manipulation to create a container div, add a simple class to it. I'll leave the CSS as an exercise to the viewer. Append the image that's associated with our texture to that container element, and then add a click handler. When we click on this thumbnail, we're going to swap out the texture on our gigantic viewer sphere with the texture that's associated with the thumbnail. And this will give us the basics of iterating through the gallery. Now, I should note, you can see that here, I should note that this is actually a terrible practice because normally you would want to use smaller images for the thumbnail to not impact loading time. But because we're doing everything locally here and for the sake of time, I'm skipping over that. But you can see here that as we click on each of the images, we can cycle through the various items in the gallery, and they're all viewable. At this point, the 2D site is done. We've done everything that we need to work both on desktop and mobile. But we're here for WebVR. So let's figure out how we allow people to dive into VR from here. The next thing that we're going to pull in is a utility called WebVR UI. This is a library created by the Google Creative Lab that provides a button that advertises WebVR support to your users. It will also communicate to them if they don't have WebVR support. In order to add that to our application, we need to go in, create an instance of the WebVR UI button, append it to the DOM, and then in this case, we're going to ask it what VR device it's associated with and cache that off for later use. If we switch over here now, you can see that we now have the button that normally would tell us that we can go into VR. But because we're on the desktop site, we can't actually go in yet. That is, we don't have the hardware connected, so we can't go into VR here. We would be able to go on mobile, and I'll switch over there in just a second. Now, even if we could go in, because we have the correct hardware, we haven't actually wired up any of the VR rendering yet. We'll do that with another 3JS utility called VREffect. This makes it so that the content that would normally go through the renderer and show up on screen will actually be rendered twice for a stereo view using the correct parameters that is going to query from the web VR API. We also need to update the animation loop to make sure that we can properly handle when we're in VR mode versus non-VR mode. We do this by asking the EnterVR UI if the users click the button and if it's presenting. And if so, we'll render the scene using the VREffect. Otherwise, we'll render using standard 3JS render. And then the last thing that we have to do is make sure that our standard request animation frame is actually using a VR-specific variant if it's available. This makes sure that if we're on a desktop device where the VR headset runs at a higher frame rate than your average monitor, like 75 or 90 Hertz, we're running at the same frame rate. Otherwise, the user will experience a lot of stuttering in VR and come away possibly sick. So we will update that. And at this point, we'll need to switch over to our Android device to see the rest of the experience. All right, great. So you can see on Android, we now have that nice magic window interaction mode where we're able to spin around and see the 360 photo without going into VR at all. This is great if you just want to showcase, one, that there's 360 content here because the user's natural hand motion will give them a hint that there's more to see. And two, it just gives people a preview, if they're, say, sitting on a bus and maybe don't necessarily want to blindfold themselves. However, once we hit the Enter VR button, we can now switch into VR mode. And at the moment, we're configured to use a cardboard device. And you can see that we would also get the nice stereo view. Now, these images aren't stereo, but you could render a stereo view of the scene. And it would work correctly in any cardboard device. So we've now created a basic web VR enabled 360 image viewer. But once again, in the VR side, we've only done it for a single image. And that's not great, especially when you're using mobile VR. You don't want to force the user to put the phone into a headset and take it out repeatedly in order to navigate between different elements in the scene. So ideally, we'd like to take the 2D gallery that we've created here and pull it into 3D to allow the users to select between the different thumbnails there. Now, this gets a little bit more complicated than the DOM elements, because we are dealing with 3D, so a lot more math is going to be involved. But the basics are still pretty simple. We're going to be using spheres, once again, just like the larger viewer, to represent the individual thumbnails. They're just going to be much, much smaller this time. And then down in when we're looping through our gallery, we're just going to add items to the 3D gallery as well as the 2D. That looks like this, which now we start to get into a lot of math, because it is 3D. Trig is kind of par for the course. But skipping over the exact details of what we're doing here, once again, we're creating the texture to go along with the image. We're associating it with each of our thumbnail spheres. And then the positioning code here just puts them in a semicircle around the user's waist, somewhere that's kind of non-ontrusive but easy to reach. So if we switch back over to the Android, we should now see, yeah, we have this nice semicircle of the same thumbnails, once again. Now, this is cool, but that's probably not the experience that you want to leave your users with most of the time, because we're doubling up on the thumbnails in the non-VR version. So over in the code, we'll just add one more line to say, if we're not in VR mode, let's just hide the gallery. Makes things a little bit cleaner. So next up, now, we've got the thumbnails, but we don't have a way to interact with them. And we're going to add that by using a library called RayInput. RayInput is the library that was used by the weather.com example that Megan was talking about earlier. And what it does is provide a single unified model for cardboard, daydream, or higher end desktop experiences with six-stop controllers. In all cases, it gives you a cursor and a ray that are based off of whatever the user's capabilities are and uses that to cast into the scene, find an object, and allow you to click on it. So to start off with, oops, went a little too far, to start off with, we have to instantiate the ray input. We provide it the camera so that it knows where we're looking. We set the size. That's just a little bit of bookkeeping. And then add the meshes that are associated with that into the scene. This is so that we can get the cursors that are associated with it in the ray itself. Like the VR controls, we also have to update this every animation frame to make sure that it stays in sync with the controller movement in this case. We also want to make sure that we know when we're actually selecting each of the thumbnails. So we're going to modify their opacity whenever the cursor is over the top of them. We'll start them out with a lower opacity. And then we'll have some event handling here that makes their opacity higher as the cursor hovers over them. One bit that I skipped here. We do need to loop through all of the thumbnails in our gallery and let ray input know that they are selectable. Otherwise, it will also try to select the larger sphere in the back, and that doesn't do us any good. And then finally, the last piece is that we say when we have clicked on whatever input, whatever the primary input is for our control mechanism, we're going to do the same thing that we did for the 2D gallery, which is swap out the texture for that thumbnail for the larger sphere. Now let's see how that looks on mobile. Well, let's save it first. Then we'll see how it looks on mobile. OK, so you can see, because I skipped over this, we no longer have the image spheres in 2D. But if we jump into the VR mode, and there we go, we now have a nice cursor that can swivel around and select the different spheres based on our gaze. This is, once again, a cardboard mode. And if we clicked on it, it switches the thumbnails. So now we have a fully functioning gallery that the user does not have to leave VR for in order to switch through images. Now, to demonstrate that this works correctly with more complex input mechanisms as well, we're going to come out here. Well, that's not what I wanted. Sync this up with a Daydream device. And then, the normal part of the Daydream entry flow is that we have to sync up the controller, which takes just a moment. And then we should be back into our same experience. With now, you can see a basic ray-based selection cursor that does not depend on the movement of my head, but can still be used to do basic selection from the gallery. So that's it. In about 150 lines of code, we've created an experience that works on desktop, mobile, and VR, both cardboard VR, Daydream VR. And if we were to put that on some of the larger desktop systems, it would even work with an Oculus Rift or a Vive. So let's switch back to the slides. OK, so to summarize some of the recommendations that we covered during the development, at this early stage, we should be focusing on apps that can be used in 2D with VR as an enhancement while the VR ecosystem is still growing. We should also strive to allow users to stay in VR for as long as possible, as frequently switching between the 2D and the VR modes can get a little tiring. Finally, there's a variety of input methods across all VR devices. And using library-like ray input helps normalize that into a single interaction model that's common between all modes. So now I'm going to turn it back over to Megan, who's going to tell you a little bit more about the future of Chrome and VR. Thank you, Brandon. Everything up to this point has been about bringing VR to the web. Now I want to talk about bringing the web to VR.   As we announced in the Daydream keynote this morning, we're bringing the full Chrome browser and the entire web to VR for Daydream View first. You can use the Daydream controller to navigate regular web pages and follow links. And for web VR experiences, you get transported into fully immersive worlds. You'll be able to watch videos in a large, screen, theater-like experience. Plus, Chrome and VR is the same app that you use for browsing in 2D. It shares all of your tabs, bookmarks, and history. You don't have to re-log into websites and VR, things just work. VR browsing is coming to Chrome for Android later this year. So what's next? Take a look at our web VR developer portal with some great tutorials and case studies and the helper libraries that Brandon showed us earlier. Check out the full set of web VR experiments online and consider submitting your own. You can also try out some of the web VR experiments here in person at I.O. in the experiments area. Thank you so much for joining us today. And I can't wait to see what you come up with. And keep their data secure. Firebase supports lots of different ways for your users to authenticate. If your users want to authenticate with their email address, you can build that for them. Firebase Auth has built-in functionality for third-party providers such as Facebook, Twitter, GitHub, and Google. It can also integrate with your existing account system if you have one. You're given the choice about how to present login to the user. You can build your own interface or you can take advantage of our open-source UI, which is fully customizable and incorporates years of Google's experience in building simple sign-in UX. No matter which one you use, once a user authenticates, three things happen. Information about the user is returned to the device via callbacks. This allows you to personalize your app's user experience for that specific user. The user information contains a unique ID which is guaranteed to be distinct across all providers, never changing for a specific authenticated user. This unique ID is used to identify your user and what parts of your backend system they're authorized to access. Firebase will also manage your user's session so that users will remain logged in after the browser or application restarts. And of course, it works on Android, iOS, and the web. That's Firebase Auth, allowing you to focus on your users and not the sign-in infrastructure to support them. Thank you for joining us for today. India is coming a long way, as I just mentioned. Today, India is the second largest country in the world in terms of number of developers. Soon, it's going to be number one. What we want to invest in is actually training the faculty from your colleagues. Your potential is so great. And what Google is doing to help catalyze that innovation is it's really an exciting time for these campuses. We are really trying to provide the best possible experience to teachers in these faculty hubs because the first step to training 2 million developers is to train the teachers that are going to teach those 2 million. Industry, as of now, demands a lot of updated curriculum, developing 2 million Android developers. Being working in a technical university, you can contribute hugely on developing those million app developers. So we're excited that all the raw materials are there to create an innovation revolution in India. I really think the students are going to make some great things. And I can't wait to see what comes out. There's a lot of potential in India. And we need to take it forward. With Google, we can provide rich opportunities to all. That is the essence of Google program, which I have seen. This is a good move. And this program will definitely be useful to the students because app development is going to rule the world for the next few years, really. I welcome you all in this Android development environment. An app is basically a solution or a medium by which you get a solution for a lot more users. You have to figure out what kind of benefit the person is going to get by using an app. I feel, personally, the phone is not a phone. It is something that can change people's life. With Google, we can provide rich opportunities to all. That is the essence of Google program. There's a lot of potential in India. And we need to take it forward to get everyone to start thinking about Android and developing for Android. We're at the cusp of a revolution. We encourage you to continue learning, continue developing, and now go build some great apps. You have the talent. And that is the need. Bring it on. LinkedIn is the premier social network for professionals. We are consistently one of the top apps on Google Play. My name is Pradeepra Dash. I'm the engineering manager for LinkedIn infrastructure team for the flagship apps. We have a million plus reviews. We are consistently four plus stars. Our users are really appreciative of how stable the app is, how it really helps them bring their professional profile forward. My name is Drew Hanny. I'm on the Android infrastructure team at LinkedIn. My team is responsible for the overall health of our Android application. We take care of releases to Google Play, our testing pipeline for the app, our build pipeline. One of the tools that my team has been excited about is the APK Analyzer tool. Because we spend a lot of time paying attention to the size of our app, and it required some expertise to figure out what was causing your app to be a certain size. But now that we have the APK Analyzer tool, we've been able to expand that knowledge throughout the entire team. LinkedIn was a early adopter of Gradle. So adopting Gradle for Android was a pretty natural fit for us, because we can share a lot of our custom plug-in logic that we've built. And we can also get consistent builds across developer machines and our build service. One thing that's really benefited us is the Lint system, which has a lot of built-in checks for common problems. And we've really appreciated being able to write our own custom Lint checks for LinkedIn's specific internal libraries. With so many developers checking into the code base, one thing that helps onboarding someone new is having a consistent code style throughout the code base. Android Studio lets us find a custom code style that each developer can add and automatically have their code formatted in the LinkedIn style. One of the things that we really appreciate is the open nature of the platform, where not only has the feedback that we have given back to the developer community in Accepted, but also being worked upon. In the last couple of years, my team has become way faster, much more agile, and being able to code easier and faster on the Android Studio. This is Ask Firebase, a show where we answer all sorts of Firebase questions on whatever medium you ask questions on. We take those questions. We give you answers. It's a show. Let's get into it. Ask Firebase. Take one, Mark. Hey, folks. My name's Abe Haskins. I'm a Firebase engineer here in San Francisco, and I'm here to talk to you about Firebase for Unity. If you haven't already seen it, I did a Getting Started video for Firebase in Unity. It'll be linked below. But in the meantime, I want to talk to you about some of the great questions we've gotten since we released the Firebase SDK into general availability a few weeks ago. Also unrelated, but if you want to call me Abe, that's great.  You want to call me literally anything. I'm OK with it. As long as it starts with an A and has some letters after it, we're all good. Let's dive in. Hey, a**. That's not nice. Let's get started with the first question from my laptop battery is dead. All right, first question. On our last YouTube video about Unity, $7.01 asked, can I use this to build out WebGL games that I've developed in Unity? And the answer is no, you really can't. Since the Firebase SDK for Unity is built on the Android SDK and the iOS SDK, we don't have the ability to build out for WebGL or PlayStation 3 or some of these other platforms that Unity supports. You'll find that some of the features do work. And even in the editor, they're stubbed out, so you'll be able to use them and test your code and compile it and everything like that. But you won't get the same functionality you would if you built out the game for the device. Thanks for the question. Next question. On the Firebase mailing list, DeepPixel asked, why does my app report libapp.so cannot be found? This is actually a great question and it's related to another issue we've gotten on GitHub from Secura777. He said, why is Firebase app not able to be found? These issues are both related to the timing of when Unity imports the dependencies it requires to run the Firebase SDK. In some environments, you're just gonna luck out and you're not gonna have to deal with this, but it just depends on your specific game and your specific operating system. So what you can do is, one, make sure you're on the latest version of the Unity plugin. We've pushed some changes that should help this for a lot of people. And two, you can go into the assets folder and go to play service resolver, Android resolver settings, and there's an enable auto resolution checkbox. This is just gonna do some extra things to help you get those dependencies imported and help control that timing so everything's gonna work well for you. All right, and if you do that, your issue should be solved. Thanks a ton, Secura, and DeepPixel. Next question. The next question is one we've been getting a ton. A lot of people want to upload different assets for their game into Firebase Cloud Storage. They wanna upload images and text, all of these great things, movies, et cetera, et cetera, all those things you need for your game, you know, you know the stuff. They wanna upload those into Cloud Storage and then retrieve them in their game and just use them in the easiest possible way. The Firebase Unity SDK is really good when you're dealing with these storage assets because you have a lot of control. You can download them as streams or you can download them as a byte array. But you don't have to really do all of that if you're not interested in dealing with those more complex flows. The absolute easiest way to get an asset out of Cloud Storage rendered or consumed in your game in some way is to use the triple W package and the get download URL async method that we offer.  It's a public URL that you can consume and pull down just like any other URL. And the triple W package in Unity makes it really, really simple to take that URL, download it and turn it into a material, turn it into an audio clip, even a movie. So any of those assets you upload in Cloud Storage or your users upload, if they're uploading profile icons or anything like that, just pull them down with triple W and you'll be good to go. If you want to find out more about the triple W package or how it works with the get download URL async method, you can check out our documentation or the Unity documentation. Both are great resources for this. Thanks a ton for that question, everyone who asked it. I appreciate it. Next question. You guys have no idea how much I spend on conditioner to get this volume. And the final question, the big one we all want to hear about Cloud Functions. I'm sure you heard at Google Next we announced this awesome thing, Cloud Functions. How does that work with Unity? What can we do with these two things put together? And the answer is you can do like anything. If you check out the repo we have, we have a function sample repo and this isn't specific to Unity. It's just our general function sample repo. It has 26 different examples of things that functions can do. And every single one of those you could do with the Firebase SDK and Unity with Cloud Functions. And that's because Cloud Functions ties into your real-time database. It ties into analytics and storage and all these other Firebase services that are supported in Unity and it lets you execute code and change things way back on our Cloud so you don't even have to think about it.  So you can absolutely use Cloud Functions with Unity and it's highly, highly recommended. So go check out that repository. It's got a ton of different samples and every single one will work great with Unity. Thanks for the question. All right, everyone, thanks for watching. That's gonna wrap up this Ask Firebase featuring Unity. If you have any more questions, obviously, you can leave them in the YouTube comments below, post them on Stack Overflow, tweet them on Twitter with that hashtag AskFirebase or literally reach out to us in any way. Tweet at me, tweet at Firebase. Shout it at the top of your lungs near a Google office. We will hear it. We will answer your questions. We'll see you next time. Oh, hi. I'm just building a paper prototype here in the design tent and I'm gonna get this design reviewed in a few minutes, but first let's take a look around, shall we? All right. Hey, Liam, how are things? Good, how about you? Doing great. I'm gonna get a design review. I'm a little nervous, but we'll save that for a few minutes from now. Before I get there, I kinda wanna know what's going on with these posters. It looks like you had a design sprint. Sure, so these posters are kind of showcasing the work that we did at the dimension sprint up in SF. It was a three-day event where Google brought a bunch of designers and developers together to kind of ideate and come up with some solutions around these five categories. Cool. Can we see some of this stuff? What is this? Sure. So this one is called Bay Window and basically the idea is to keep two people connected to each other. It's a live wallpaper in the last three. Oh, I get it. Bay, like BAE. Okay. Yeah, exactly. So you basically pair with your partner and then you can upload photos directly to their live wallpaper. And if you go a while without connecting, then you kind of see these two circles like start to drift away a little. Oh, that's not good. All right. What's another one of your favorites up here? I think this one is probably one of my most favorites out there. It's around self-activation. And the idea here is just kind of a community where you can share experiences and get some support. It's made to kind of serve the needs of people who are looking for a safe place to talk about experiences they've had, kind of document their own progress of working through those at their own pace and just keep track of how they're doing. So it kind of allows you to like track how you're feeling each day, some thoughts or experiences you might have through