A developer's guide to rendering performance by Paul Lewis
This is the hashtag that we normally use when members of my sub team-- so I work on Chrome Developer relations.
Part of the team is focused entirely on performance.
When we talk about performance on Twitter, and so forth, we include that tag.
If you want to include that tag, that means that my team will see it as well.
If you don't, that's cool, I'll still pick it up on frontiers 13 as well.
So I actually want to reset the performance scene a little bit.
Steve did a really good job this morning of talking about the network side of things.
Alex has just done some of the same.
But I kind of feel like when I look at this stuff, historically, we've kind of gone, how long did this take to load? How many requests were there? How big were the images? Did I have gzip on eTags? All this kind of stuff.
And that's absolutely crucial.
Let me say that again.
It's absolutely crucial that we do that.
However, I do feel like there is a bit of a question mark in our minds over the rendering side of things.
Like, the job of the browser is to get pixels onto your screen.
That's it's job.
And so it's kind of important that we understand the entire length of that process.
And so that's what I'm going to try to-- I figure there's loads about the first part, the page load.
I really want to talk about that in a second part.
I am going to talk about this from a Chrome-y perspective.
But the thing is, most modern browsers go through a very similar process.
They do have slightly different names for certain things.
But the idea of how they get the pixels to the screen is very much the same.
So we're going to start through, we're going to go through this primer.
And then we're going to look at some of the tooling around it.
So we start off with our humble get request.
And our server is going to respond, unsurprisingly, with some HTML.
Surprise, we get tag soup.
So the browser, of course, has to do this parsing.
It does, as we discovered, some very clever things about look ahead and so forth.
But really, what we care about is the fact that it's pausing.
Dev tools is going to show you this as Parse HTML whenever that crops up.
So if you shove something in the DOM as a string with inner HTML-- Parse HTML.
Now, we construct ourselves a tree off the back of this, which looks like this, in this case.
Now let's just call that the DOM because it's easier than keeping that tree around.
And the next step that we actually have is the CSS.
And that's going to come from the user agent stylesheets, the inline styles, your stylesheets, any of the plugins that you put in.
And we're going to combine these two.
And this shows up in Dev Tools as Recalculate Styles.
And we basically combine the two.
And there is a sound effect for this, which looks like [FART SOUND].
Not a lot of people know that.
If you actually listen to Chrome very carefully, that's the sound it makes.
That's not true.
So our render tree looks somewhat similar to our DOM-- except the things that are missing, right? Head is switched off.
Script is switched off.
This kind of looks like a DOM, but it's not our DOM.
It's our style DOM.
So if we have some CSS that says don't display the section paragraph.
Why did you include it? I don't know.
There we go.
That disappears from our render tree.
If we are including pseudo elements, that's going to appear right after in the tree.
But of course, we have a lot of CSS.
The actual CSS in place here doesn't really matter too much.
And this is the process that you saw with the animation before for the Reflow in Mozilla.
We call this layout in Blink and webkit land.
And it kind of looks like this, really.
Geometry for the page.
It's basically figuring out where all the boxes are on screen.
And I can't impress upon you the cost of layout enough.
It is extremely expensive.
The bigger the DOM, the more expensive the layout.
If you change something, we're probably going to have to recalculate a lot of the documents.
So we really want to avoid that when we can.
So layout is a big deal, as you'll see in a little bit.
OK, so we have those boxes.
Sidestep for a little bit of terminology for a rasterizer.
Those boxes are effectively vector shapes.
They're not actually sort of mapped to pixels yet.
We know the x, y, width, height kind of data.
But what we want to do is get to the pixels that we have on screen.
And a rasterizer is a piece of software that will help you do that.
It will get you from a vector representation like a box through to some pixels on screen.
Step back in-- oh, actually do the rasterizer thing a bit more.
If you and I were to sit down and make a rasterizer, this is what we would include.
I know this because this is the rasterizer from Chrome.
It's called Skia.
So you've got things like drawing points, drawing paths, drawing text, lines, a bit of moving around stuff like the translates, and saving and restore.
If you've worked with the canvas, you probably recognize it.
It's effectively a rasterizer.
So let's step back now.
With that HTML that I had, and those boxes I sent to Skia, what did you do? How did you actually get those pictures on screen? And it went ta-da! And I said that's a bit too quick, Skia.
I need an instant replay.
So as we step through each of these individual calls, you'll see the picture building up behind me.
We're adding the text.
We go through.
We draw some paths with the box shadows, clip some stuff, draw a picture of me, restore, translate, restore we're done.
Now, Dev Tools is going to show you this as paint.
Now one thing I want to point out is operation number 15, which is draw bitmap.
And I've been a very, very good person.
I sent a really, really small JPEG down the wire.
Yes, go me.
Thing is, it's draw bitmap.
It's not draw JPEG, or draw PNG, or draw GIF.
Hands up for GIF? Hands up for JIF.
They're not jraphics, are they? All right? [LAUGHTER] Some people go, well, the creator of the GIF said was it a JIF.
So we actually have to go through this process of decoding that JPEG out to a bitmap in memory, which is a slow and cumbersome process, often.
In fact, it's extremely expensive to get from that encoded form to a bitmap.
And of course, we're probably doing something like responsive.
So what we're is going to do is we're going to resize images down.
Dev Tools again.
If you spin down those paint records, you'll see image decodes and resizes.
And we'll look at this more in a bit.
Now, when we were doing that rasterization, that painting, you may have realized that we have sort of effectively over drawing the pixels over and over.
Like we just sort of fill it in as we go from back to front.
What happens, then, if we need to, say, move this picture up here? Well, what happens is the browser chucks an area around it.
It says, right, this bit is damaged.
And so now we need to step through and re-rasterize step, by step, by step.
And of course, it doesn't take much imagination to start going, huh, what if there's a lot of elements that need to be repainted as a consequence of what I did, what I moved? Thankfully, there is a fix for this, as it were, this behavior, which is the layer.
The humble layer.
The idea is you can rasterize, you can paint into different surfaces.
So this is, actually, my site.
And the masthead carousely thing is essentially its own layer.
So it gets rasterized independently.
It gets painted independently of the site behind it.
So if you sort of look behind it, you can see the pattern is still there and so forth.
Chrome is going to show you this in Dev Tools as composited layers.
This is the idea where it goes, how many layers do I need? Making sure they're all in the right place, putting them all back together.
That's composite layers.
Now, as a side effect of creating a layer, the Next and Previous buttons are actually DOM elements on the top of my canvas.
And they actually have to be promoted to their own layers to preserve the depth order.
Otherwise they'd be on that back one, and they are lost.
So put it all back together, composite layers.
So the layers thing is basically how we get around this problem.
So you might be asking yourself, well, how do I create a layer? Fair enough.
The most common one, listed at the top, is the 3D transform.
So translate 3D 0 or translate 3D 0 0 0 is the one that people often use to force layer creation.
Videos, canvases, compositive pluggers, like flash Silverlight, animations on opacity, transforms, filters.
And the last one is that Next and Previous button.
It got rendered on top of something that was already a layer.
So we had to make a layer for it.
This is very implementation-specific.
I include it because people are often interested and want to know.
But it is a thing.
I've put the subject to change because it's an implementation detail.
It's not something you're going to find in a spec.
So we've done all our painting.
Great for us.
Just for completeness sake, I'm going to tell you we actually paint into tiles.
It doesn't change anything we do.
I just like to include it because I'm a sucker for the details.
All of this happened on the CPU side.
Up to this point, we haven't hardware accelerated anything, particularly, probably, most likely not, unless it's a video or a canvas, in which case, those are the most likely exceptions.
But everything on the CPU now.
Now all we have to do-- and this is composite layers again-- upload all those tiles across the GPU.
And don't be fooled by the animation.
If the link between the CPU and the GPU is not good, like it's often not on, say, a mobile device, you're going to pay a tax for uploading that texture.
So if you constantly repaint, and you upload those textures over, and over, and over again, that's going to be quite bad.
It's going to be bad for that sort of transfer.
Well, it's bad to paint in the first place, often, because it's expensive.
It's bad to transfer it because that's often expensive.
So you want to avoid it, really.
Anyway, boop! There we go.
That's how we got pixels from the get request to pixels on screen.
I love this phrase.
I could give you a bunch of rules.
I'd love to.
And to be honest, there is some stuff in here in this presentation that is a bit sort of rules-y.
But I'd actually much rather that everybody left going, I'm going to get into to the tools.
I'm going to learn to profile my site, my application.
One thing is great.
It's in vogue one year.
It's not the next.
Just start using the tools.
The profilers won't lie to you.
You can adjust to what they tell you if it changes in implementation.
Tools, not rules.
And it rhymes.
Which Jake Archibald tells me, if it rhymes, it's true.
Tells you a lot about Jake.
Now, Alex already mentioned this, but 100 milliseconds is the number we often assign.
There are two numbers here.
The first one is, like, if I do something once, like I resize something once, how fast does that need to be? 100 milliseconds is going to make it feel instant to the user.
But we also have the other number that we all know, probably, I guess, 60 Hertz, 60 frames a second.
1 second divided by 60 gives you 16 milliseconds.
Now, this is going to apply to interactions like scrolling, like dragging, animations, transitions, that kind of stuff.
So these are our two numbers.
So as we step through the tools, which we're about to do-- I'll kind of remind you as we go.
But these are the two numbers that we care about.
Some stuff we're going to care about getting inside 100 milliseconds.
Other the stuff, inside 60.
So styles and layout.
I have here Chrome Dev Tools Open, with the timeline set to frames.
I've pressed the record button at the bottom.
And I have a demo here.
Left hand column, right hand column.
The left hand column, when I click this switch layout, it's going to animate.
So that's an amazing transition.
Well done, Paul.
And back again.
Now we're going to switch back to Dev Tools.
And we're going to see what it made of that whole debacle.
Now, the thing is, it's telling me straight away that recalculating the style, which was actually the left column and the right column, just getting new widths, I'd set a transition on them, actually, as it happens.
There was just me sort of, basically, setting a couple of values.
Actually, changing a couple of classes.
The widths were actually in the CSS.
Two elements affected, fraction of a millisecond.
Thumbs up for us.
The problem now comes when we actually look at the consequences of our actions from the layout perspective.
We wanted 16.
We got 35.
We're not happy.
We had to do the whole document just because, basically, the elements we were dealing with were actually quite high up.
And there isn't much we can do at this point.
This is what I was saying earlier.
It's a very expensive thing to do layout.
It's not something you're probably going to get away with inside an animation today, most likely, and especially, bear in mind, 35 milliseconds on desktop.
On mobile, expect five, six times-ish more expensive.
But 60 frames a second is still your limit.
So keep that one in your heads.
So if you've not used this tool, by the way, you're looking for all those bars along the top to get under that 60 frames a second line, if you can.
So styles and layout, then.
Small style changes, big layouts.
I think that's a really key point.
You can trigger layout with CSS by changing a width or having a transition that's triggered on hover.
The whole point is, if you change the geometry, we're going to trigger some layout or reflow.
Layout isn't painting.
And it sounds like an obvious thing to say.
But if you change the geometry of the page, sometimes you're going to repaint, especially if things were grouped together, like when I moved the picture of me.
But sometimes, you're actually not.
So it's kind of helpful to then say, OK, which styles affect layout.
And it's pretty much these, really.
This is in order.
As you go down the left column and the right column, width, height, padding, margin.
Just let that soak in.
Carrying on, then, let's do another record with another demo.
Let's do some layout thrashing for funs.
Change all these paragraphs to match the width of the green block.
That was horrendously long time.
If you didn't notice it, don't worry because we're going to actually have a look how long it took.
I have a sneaking suspicion about 1 and 1/2 seconds.
Yeah, that's right.
1 and 1/2 seconds.
Again, we were looking for 100 milliseconds.
We got 1 and 1/2.
Not doing so great there.
So let's find out why.
Well, of course, you already know, I'm sure, we calculated a style.
Then we did some layout, and so on.
Let's go back, re-record.
And let's do it properly this time.
You could do it right.
Ta-da! Now, we wanted 100 milliseconds because it was just a single resize thing.
It's not animating.
We got 39 milliseconds.
Pat on the back for us, super.
Despite the size of the layout, that was actually OK in this case.
So let's have a look a little bit more why.
Again, we already know, I think.
But this is effectively a read write, read write, read write kind of situation.
So each write is going to basically save the last layer you did.
Just got to check that out and do it again.
So you go around the loop and it goes, oh, I'd better do a layout for this one.
Can you change that paragraph for me? Super.
That's going to invalidate the layout I just did.
And so on.
You know that.
Great, so let's do it right instead.
Let's move asking for the block width, which isn't going to change.
Let's move it out.
Read, write, write, so you want to batch it in this form.
Now FastDom, new library by a guy called Wilson Page.
Definitely worth taking a look at because that demo I just showed is actually pretty trivial.
And you might be saying, well, I'm not going to get caught up by that.
Of course not.
In a complex code base, it's possible you could have unexpected interactions.
So what FastDom does is, it basically makes it convenient to batch up.
It will redo that for you.
You might want to look at it.
It might just be something you want to have a look at.
Here is it running, just in case you wanted to see layout thrashing really destroy things.
Oh, and another tool, that FPS meter inside Dev Tools.
Frames per second.
So we have this Forced Synchronous Layout button pressed, which is the one that's basically layout thrashing.
And we have our little animation running at 9 frames a second.
That's not good, is it? Good.
So let's go with FastDom for a split second, see what it can do.
Imagine the difference.
OK, it's not quite 60.
I was doing screen capture at the time.
But what's the frames per second between friends.
We good? So that's the frames per second FPS meter.
That's a really useful tool if you just want a quick "how am I doing" without getting too knee-deep in what are my tools, what's my timeline, what's all that going on? Paint then, we'll look at that.
So what have we got.
We have a tool here.
This enables continues page repainting, which is basically going to tell us, if we were to paint this page from scratch, if we were going to rasterize everything from scratch, what would the time be? And it's currently four milliseconds to do all that.
So that's cool.
Let's add something expensive like a box shadow.
Now we've dumped it to 40 milliseconds to paint this page.
And we can switch it off and on.
And we get a sense of, OK, when I do this, yeah, OK.
It's pretty obvious.
Because I'm in control.
I can see exactly what I'm doing.
But if you look at it in the other direction, where you've got something that is expensive to paint, you go to your project now, you switch this on.
And it says, this will take me 100 milliseconds to paint.
This is the position you are now in, effectively, right? So the thing that we can do, which is actually really neat in Dev Tools, you can hit the H-key on your keyboard.
And it will start hiding things.
So you can start stepping through, switching off individual elements, or you could even go down to the styles there, switch off some styles.
And you can actually watch what happens to your paint time.
It's extremely useful for kind of finding do I have an expensive element.
Let's summarize then.
Finding expensive to paint elements.
You can hide them, nice and quick, and watch what happens to your paint time.
See, we've got loads of tools in Dev Tools for all this.
When I select that portion of the page there, Chrome says well, I have to do the highlight on that text.
So I'm going to chuck a red box around it, let you know that there's a paint that happened there.
When I scroll the page, you'll see there's one on the scroll bar as well.
This is cool.
So what I'm going to do is I'm going to do something really bizarre.
I'm just going to animate these all in a circle.
Why wouldn't you? It's like every project you've ever done.
But you'll notice I've got one big, red box.
And this tells me that everything, all those boxes, were actually all grouped together because they all got invalidated by the animation.
And so we had to check a big red box around it and say, this all needs repainting in one go.
Now this is where the translate Z hack-- it is a hack.
I wish we didn't have to use it-- but it does come in handy here because we can promote all these boxes to their own layers.
And now, we've basically asked the GPU to just move them around for us.
So instead of repainting, we're not painting anymore.
We painted once.
We've asked the GPU to move those layers around.
Now there is a downside to this tale, which is, if I now animate each of these boxes, and I'm animating the border radius, you'll see, I get a bunch of red boxes.
Each one of these layers now needs to be individually repainted and uploaded to the GPU.
So it's a balancing act.
OK? It's very much a balancing act.
But this tool is brilliant to kind of highlight to you, did I paint something on the screen? How big was that paint area? So that's important.
So check your paint areas.
If you have the entire viewport in red, and you're painting everything, which is often the case when you've got something like a fixed position element.
And I think that's something that we're going to try and get better on, you'll probably see big flashes of red.
That should be an alarm bell for you.
I would like to go and fix that should be the answer in your own mind when you see that.
You can promote elements to their own layers to isolate them.
So if you're going to move something around, and it's kind of affecting all the other elements, you probably want to requestAnimationFrame.
That's an interesting one.
Anybody use Jquery's animate? Yeah It uses setInverval today.
There is a patch available for it to not.
If you're interested, I got that from the Jquery source.
It's one of those things.
But it's interesting because, obviously, we're all dependent on libraries.
We don't typically write everything from scratch.
Is the library doing something that you wouldn't do? It's an interesting question.
Again, the profilers should tell you that and let you know.
So requestAnimationFrame, it's a good thing.
Touch handlers, not a lot of information about this one out in the world.
Largely, again, it's an implementation detail.
But your average modern browser isn't just one thread, oddly enough.
In Chrome, well, basically, there's a compositor thread.
Now, the compositor thread's job, in this instance, is to basically move those pictures around.
So we did all the painting.
We have all those pictures ready to go.
You do a bit of scrolling.
And the compositor thread goes, I could just move those pictures a bit.
Move them up.
Move them down.
I'll deal with this.
So what's the problem, then? The problem is this.
We've added a touch start handler.
And what happens is, the compositor thread gets told about scroll or fling action, for example.
And it goes, ah, I can't deal with this straight away by myself because there's a touch start handler.
And the developer might have put event.preventDefault
on that, which would mean I shouldn't scroll.
I should wait for the main thread to tell me what the response was.
Now the problem is, the main thread is then busy doing some layout or such.
And our touch start is queued up for the end of that.
And it comes back.
Meanwhile, the compositor thread was blocked.
It couldn't do anything except wait for the answer.
What does your end user-- well, they just feel it stick.
Why is this not scrolling? Of course, you get bug reports.
And you feel sad.
Who doesn't? Oh, bugs.
So this is something.
This is interesting for me.
I'm actually interested as to how we can get around this.
This is sort of an architectural thing.
I appreciate that.
It is subject to change.
It is very implementation specific.
But it is a real thing.
If the main thread can't answer the question, the compositor thread can't scroll the page.
Interestingly, there's a 200 millisecond timeout, after which a touch cancel will be issued.
So if you were relying on touch start or touch hand or touch move, and you got a touch cancel, what do you do? So touch handlers, then, what can we do? Well, we can keep handlers down to a minimum.
That's a good thing.
If you can do what you're trying to do without doing a touch handler, do it without a touch handler today.
I'd rather I didn't have to say that, but it's true.
If you can avoid them, avoid them.
Bind close to the element.
Now, I come from, oddly enough, a flash background, where what you used to do is, you used to put everything.
You used to handle stuff like this at the root.
So I when I do touch handlers, I was like, bind it to document.
And then I'll get them on the way up.
And then I'll figure out which elements it was.
And then we'll be good.
The problem is, most of my handlers actually concern an element that's quite far down in the DOM.
So the thing about this is, if you bind closer to the element, there will be times where we can actually be a bit more intelligent and go, oh, I didn't need to check with the main thread because I'm pretty sure that the thing that was touched is not the thing that's got a touch handler on.
If it's always attached to the document, well, it's always going to get fired, even if we didn't need to do it.
So if you bind a bit closer, you might find that, sometimes, you can avoid the work.
Yay! Bind late.
Sometimes, again, I would just go, just check it on the document and forget about it.
In that situation, there may be a view on top or something like that that means I could never trigger the touch handler in a meaningful way.
And it's just say there going, I'm just listening to touches.
Don't need me.
Take it off.
Just remove it.
It's basically, this is all about getting rid of touch handlers whenever you don't need them.
There you go.
Whenever you scroll, scrolling causes pains because the new content appears on the bottom of the screen.
And we have to paint it, those tiles.
That's where the tiling thing comes in.
Because of the fact that it involves painting, avoid changing anything that you can during a scroll.
So you might want to, as Alex said, debounce your scroll handlers.
His looked a little more elegant than mine.
Mine looks a bit like this, which is basically to say, just get the value, store it for later, and always schedule a requestAnimationFrame to deal with it.
So even if you get 5, 10, 100, whatever, just grab the last one, and then, this is great.
The last thing you want to do in this on scroll handler is change anything at all on the page, if I'm not clear enough.
We see this a lot when we're profiling parallax sites.
Yeah, you know who you are.
I'm sorry, governor, I didn't mean anything by it.
Create the ready site if everything moves.
Everyone will love it.
And for bonus points, when they scroll up and down, it goes left and right.
You know it.
I know it.
At least if you debounce it, you've got a better shot at actually success, rather than a jerky, horrible mess.
Doesn't mean you should do it.
Yeah, this is for bonus points here.
I get really stressed when I see this slide.
It's all right.
It's just a GIF.
It's not actually loading anything.
My stuff appears over the top.
I am proud of me.
My application loaded.
The thing I forgot to do is, I forgot to switch off the animated GIF.
You know what I'm going to tell you, right? The browsers all keep painting, with exception of Firefox.
Well done, Mozilla.
You got it right.
When Firefox goes, ah, that's covered up, I'm not going to bother painting that GIF, they did that right.
Every other browser far, as far as I can tell, actually, goes, you know, I should just repaint everything.
Certainly the GIF bit.
I tried to check with IE11.
I couldn't get an answer from them.
That one's a bit of an unknown for me.
I don't know whether IE gets it right or not.
But certainly Firefox does.
So thumbs up for them.
Finally, to kind of close out, I just want to bust a few myths wide open.
And that's for good reason.
It is what we think of most often when we talk about front end and performance.
But it itself, the work is done in C++.
Start treating them with deep, unyielding cynicism.
And just, go, I can't stand this.
Because, the reason is, let's say a new browser comes out tomorrow, a new handset.
And it seems like, I feel like, it's the only metric that is sort of bandied around.
The DOM is slow.
Oddly enough, actually, talking to the DOM is pretty fast.
The problem is, let's say you chucked an extra DOM element in there.
Well, of course, you've resulted in a lot of extra work.
Probably Parse HTML, if it was inner HTML.
Then we're going to do some calc styles.
Then we're going to do some layout.
Then we're going to do some paint.
Then we're going to do some composite.
That's why it's slow.
It's not that the DOM itself is slow.
It's the side effect of talking and changing things in the DOM.
So I'm going to close out a little bit early.
Loads of times for questions.
Performance is a feature, not a unit test.
I like to trot this one out because, if you wait to the end of your project to go, are we fast? No! Then you've got a problem.
Because now what are you going to do? Are you going to ship something that's slow? Maybe.
But you certainly, probably, are not going to go back and rip out all that code because that took you months to build.
It's something you prioritize alongside your other features.
It might be number one.
It might be number five.
If you can just start treating it as a feature, that's good.
As you've hopefully guessed from all this, you are actually in the driving seat.
The browser does do a lot of work.
But you are in the driving seat.
You get to control an awful lot of what it's doing around this.
Actually, it's interesting.
Talk to your designers.
Maybe you can drop the drop shadow.
They're actually all fond of flat right now.
It's playing right into our court.
Hey look, I made it go faster.
What did you do? Dropped the box shadow.
Drop shadow, incidentally, is actually a lot faster.
And it could get even faster again, I guess.
So don't be too mean on drop shadow or box shadow.
I see a lot of people do this one, or the inverse or converse of this one, where they go, I've really concentrated on my for loops and my while loops.
They are fast.
But that's not their bottleneck.
Profile your app and then fix the problem you have.
Don't fix the thing you think you're going to have before you've actually profiled it.
That's not the way to do it.
It's kind of the like the first one, actually.
Profile early, profile often.
It's a good thing to do.
Tools not rules.
Thank you very much.
[APPLAUSE] There is tons of stuff at jankfree.org, like talks,
and slides, and whatnot.
And I reckon it's time for the questions.
It's really hot.
That's all it took.
That was great.
Thank you very much.
There's a lot you covered.
First up, let's talk about touch.
You talked about touch handlers, black and scrolling.
The rule of touch handlers, black and scrolling, does that also apply to click handlers, the click event? Yes, but for different reasons.
Actually, a bug opened about click handlers today, which is about if you have expensive work that happens inside a click handlers, everything, everything gets paused until the click handler has resolved.
So if, for some reason, you just decide to do a bunch of work.
But is it analogous to the touch? I'm trying to remember off the top of my head.
I don't think it is.
I don't think it is.
No, I think it's just that touch handlers are done inside the compositor thread.
Does click follow a mouse up? Yeah.
I think it does.
I think so.
I think it does.
But in any case-- So the order is, it goes through all the touches, and then it goes into mouse down because on touch screens, all the mouse events are emulated.
And then when, after mouse up, it finally fires click.
But anyways, that order means that, basically, the browser-- the whole thing with touch is that when you put your finger down, your scroll the browser needs to know, do I have to do work before I show an update.
Yeah, exactly right.
But there is, as I say, there is an interesting side effect that's all on the click handler stuff.
If you are going to do heavy work inside a click handler, it's very much like the onscroll stuff.
Defer it with a RequestAnimationFrame.
Deal with it later.
Because it allows the click handler to end and other events to get handled.
Basically, the reason that Chrome can't exit the click handler is because it has to preserve event order.
So it has to make sure that the click event handle was finished before it starts doing any more work.
It's an interesting one.
It's a bit freaky.
Should we just avoid box shadow? No.
So I can use it? Yeah.
Just know what you're getting into.
I mean, like, the painting, those tiles that got painted, we keep them in memory.
So we pay that cost once.
When it gets combined with other styles, sometimes it gets more expensive.
But no, don't shy away from it.
Just profile it.
Figure out, is this styling worth the painting cost that we're going to pay? That's very individual to your project.
That would be a rule, wouldn't it? That would be like going, we will never use box shadow.
And I think, if you walk away with that, then that's not what I want you to do because the algorithm, the implementation for a box shadow could change tomorrow.
And all of the sudden, that's old advice, isn't it? It's rubbish.
Similarly, someone asked about, is there any advantage to doing complex animations inside of shadow DOM.
Would those be seen as a separate layer? So the only time, as far as I know, that that would be advantageous is, OK, so this concept of layout boundaries.
And it's the idea of how far up the tree do we have to go before we're confident that nothing else has been affected by this layout change.
And most of the time, you'll see, actually, in Dev Tools, it gives you the scope and the root of a layout operation.
And an awful lot of times, you'll see that it's the whole document.
So you can create layout boundaries.
I didn't include that in this.
But I have a library called Boundarizr, which actually puts on boxes around your elements and says, these could act as layout boundaries.
So if you animate something inside of there, it might only need to go up to that element before it can say, I don't need to go any further.
Adding it to Dev Tools, so when you click on an element, you should be able to, with a context menu, hopefully, be able to, say, jump to the layout boundary for this element.
The question is, is shadow DOM going to help? As far as I know, shadow DOM won't be acting as a layout boundary.
It also won't help with any sort of paints.
I think this goes back to the difference between DOM tree and render tree, where render tree is mostly what we're concerned with here and DOM tree is a separate concern.
Yes, basically, because all that layout, the geometry and stuff, was all inside the render tree, as far as we're concerned.
Bruce Lawson has a question around image decoding costs.
WebP versus JPEG? Is 1.4 times I believe,
at the last count.
So yeah, that's an interesting one.
I know that the WebP team are very keen to reduce that as far as humanly possible.
There was a really big thread on the blink-dev mailing list, when an animated WebP, that thread.
And they went through and landed a bunch of optimizations to improve the decode time of animated web WebP.
And that happened after that 1.4 benchmark came out.
So yeah, watch this space.
The chances are, any decode is going to be completely overshadowed by resize operations that you do.
So even if you spend, it's like the difference in 5 or 6 milliseconds on a decode, your corresponding resize for something might be sort of between 20 and 30, which is going to happen at the bitmap level because you decoded to a bitmap.
So at that point, the resize doesn't matter, whether it came from a JPEG, or a PNG, or a GIF, or WebP.
So taking into consideration everything that you talked about as far as the image resize and decode, what is the most appropriate way to handle responsive images and high DPI? Easy.
Piece of cake question.
I'm going to say, I honestly, at this point, don't know.
Because we are paying the resize cost on the client side.
But maybe you, I mean, do you? No.
I was going to-- I thought that there was, doing a scalar resize going from a factor, so like, going from, like, two to one.
What, like, 2.04 is
thumbs up compared to 1.1?
Apparently, apparently not.
I asked the engineers.
Because I was like, this question will come up.
Because this is that technique of setting down the really big image, so it looks good on retina.
And then the lower just kind of scales it.
So the actual question I asked our engineers was, let's say, it's like 2 times versus 1.1 times.
My assumption is, 1.1
times is actually better because there's less resizing.
And the engineer went, it's complicated.
Sometimes we'll just kind of tweak things.
Sometimes we'll go all out.
Sometimes we might need to resample.
Sometimes we won't.
The honest answer is, the data doesn't exist yet, is the honest answer.
And this is why, when we're looking at these proposals for responsive images, which I think is a great thing, generally, I would love for us to make sure that we include the impact on rendering as part of that assessment as well to kind of go, what does this actually mean all the way through from request to pixels? But today, I don't think we've got the data.
I guess it seems-- Speak your mind, Paul.
Feel the answer.
We've talked about this a few times when it comes to imagery size and how it's killer.
But it's extremely hard to do a responsive site without scaling images at all.
Because across screen sizes that you're targeting, you're going to have to end up with images that move.
And you can create 50 different assets, I suppose.
So I actually wonder whether final solution will end up being, hey, paint everything, but treat my images as a lower priority item because they're so expensive.
Lets resize, get everything else painted-- mainly the text so people can read the stuff that's on the page.
And then, when we've got a spare few milliseconds, don't block the painting of everything else on this.
Just, when that's all done, now paint my images.
I would love for us to see that.
But there's discussion of a lazy load on images.
But, at the moment, there's no discussion on a lazy decode.
And I probably need to kick that off, I guess, to say, how about a decode and a lazy resize thing as well.
Well, I guess they're kind of related.
And you don't want to send a decoder without resized-- anyway.
Be declarative on the HTML? Why not.
Let's go all in.
One more question on images.
What is more efficient as far as rendering costs.
A single sprite or non-sprited images.
From which perspective, I guess? Because from a network perspective, that's obvious, right? The number of requests.
But from a painty paint point of view, it should be-- go on? You look like you are poised to answer your own question.
No, you answer this.
I'm going to ask a follow up.
Don't let me go in your way.
My hunch is, and it is only a hunch is, it would be, the sprite would be better.
Because, of something from a memory point of view, you wouldn't have the overhead of individual images with their attached decoded versions.
You would have one image with one decoded version.
But the actual number of bytes for red, green, blue, alpha would be the same.
And so the follow up there is, if I am using a sprite, like single sprite image, using it in five locations, my memory consumption for that is going to be just once even though it is displayed five times.
So from an implementation point of view, we carry an image cache which has the encoded version inside the cache and then the most recently used decoded version-- decoded and resized.
So something like a sprite is probably not going to get resized.
So, in that situation, yeah, you would expect to see that you are paying that cost for that single image.
And it's just being re-used around the place.
That would be my guess.
When you showed before the composited layers, and kind of the order of those, how is that managed? And is there any relation between what we saw there and z-index? So again, this is actually a question that I asked.
And it turns out there are two kinds of layers.
There are layers at the WebKit and Blink level, which is where stacking contexts and all those kinds of things come in, which is where z-index is going to change and create those kind of conceptual stacking contexts.
But that doesn't necessarily correlate to the number of compositor layers.
So when we've got this sort of tree of all the things that need to get painted, that gets often sent over to the compositor for the compositor to go, well, actually, how many layers do I need to paint now to satisfy this? So I believe there can be a correlation.
But it's not necessarily an obvious one.
There are a lot of edge cases.
And it is a complex beast.
It's not like, do this.
One question that comes up a lot.
So the use of promoting a layer, so applying, for instance, a translated z onto a layer, onto an element to turn it into a layer, what is the cross browser story with that? Yeah.
So it is a very specific thing to the WebKit blink.
So it is unique.
To some degree, it's unique to Safari, and Chrome, and Opera.
Because there are changes that can happen in Safari that are different to Chrome, obviously.
There's no guarantee that that list of criteria about creating a layer will either stay the same in Chrome and Opera, or that it would match up entirely to Safari.
It's an implementation detail.
So what was the question again? Whether people should do it? Oh, the cross browser story.
Firefox, as far as I know, their reasons for promoting are similar but not the same.
Are they going to autopromote fixed position? Yeah, the fixed position one is actually one of the biggest ones.
The reason we don't currently promote fixed position elements is because it changes the text rendering.
And it's something that we're working on to try and-- What's the deal with that? So you can have-- thanks for asking-- two types of text rendering.
There's grayscale and subpixel.
And you will switch from subpixel to grayscale if it goes onto a non-root layer, IE, you created a layer.
The text rendering will switch from subpixel to grayscale.
A lot of people go, ooh, when they see a grayscale.
This is why we don't do it, right? So we don't ruin your text rendering.
The caveat for this is that you can't tell the difference on retina.
So we actually autopromote on retina screens.
My hope is, in the long term, that this is a non-issue.
It's a non-thing.
But as I said during the presentation, I'm pragmatic enough to realize that, most of the time, you're probably going to know better and go, actually, I know I'm going to repaint that thing.
Just put it on its own layer.
And for Chrome, Safari, and Opera, that's currently the way you do it, as far as I know.
A brand new question just came in.
How do icon fonts affect render time? I guess this would be, so using a font with icon, dingbats, for symbols, rather than a spreaded image.
How would you evaluate the performance difference in reflow and repaint? Reflow and repaint? I don't know.
I've not spent enough time looking at that as a comparison, if I'm completely honest.
Have you? No.
Chuck it to the other Paul.
But I would say-- layout, so the example that you showed before, where, like, the two columns with all the text that was all animating, the layout cost was enormous because it had to figure out where all the text positions are, and figure out where the new paragraphs end, and such.
So we're figuring out the geometry of all those text nodes and how much space they take up.
With an icon font, you often don't set a width and height on that.
Unless you apply inline block.
And if you do apply inline block, then it's good.
But otherwise, you have to wait for that font to load.
The font loads.
Then the browser finds out the new geometry of the page.
And then it incurs a layout.
If you're using a spreaded image, you set the size of the box already ahead of time.
That would be the one difference I know of.
Yeah, but I wouldn't want to say what kind of impact that would have.
It may be negligible.
It may be that, it doesn't make much difference.
There's so many advantages to using the font, too, because it's vector.
But I don't know.
So I wouldn't like to say.
Let me know.
Write it up.
Lastly, how did you make your slides so good looking? Thank you.
No, but actually, did you apply the things that were in your slides to your slides? Did you profile it? Did you use tools without rules? Yeah.
So the story of this is I actually started building the deck about 5 or 6 months ago.
And Paul was in London.
And he stood next to me.
And he just put a frown on.
And I was like, what? And he went, this makes me so sad, bro, because there are enough decks out there.
You don't need to make your own.
Oh, I was giving you a hard time because you were making your own slide framework.
Yeah, yeah, see how much it affected me? Feel bad.
Anyway, so I got to the end of this process.
And I was like, right, I need a deck that lets me explain this process of getting a request all the way through to the pixels and, ultimately, no deck is designed for this.
They're all just like, here's your bullet points.
Here's your bullet points.
And I was like, no, this is not going to fly.
So I ended up having to craft my own.
Yes, I did profile it.
But I actually went crazy for the stuff that I talked about in there, like, using transforms was one of the things-- animating left and top because I didn't want to have layout cost.
I used canvas extensively because I know that when they're bigger than 256 pixels, they get hardware accelerated and things like that.
And it's actually, when you know you're running in Chrome for your presentation, you can be a little more specific about some stuff.
And I basically play to that a little bit.
So that's what I did.
But I did, yes.
Of course I followed the tools not rules thing.
Of course I do.
Of course you did.
Why I wouldn't I? Yeah, I believe it.
Paul, thank you very much.
Oh, thank you.