I've noticed that the more I use certain CSS3 elements (namely box-shadow and text-shadow) the more scroll lag exists on a page. I notice the problem on both FF4 and Chrome 10. Is there any good way to measure this or reduce it? I want good performance, but I also want to be able to use the shadows to create dimensionality between the various elements.
Thanks!
I've found the two biggest offenders (as far as performance goes) are the blur amount of your shadows and whether you're using any alphas (rgba, hsl, etc).
Hardware acceleration is key to using any of the css3 goodies and maintaining acceptable performance. Webkit (not sure about FF4) won't even use the GPU unless you specifically ask for a three-dimensional operation. You can kick in the GPU for any element by simply applying a 0-pixel 3d transform:
-webkit-transform: translate3d(0,0,0);
/* OR */
-webkit-transform: translateZ(0);
Paul Irish has a great talk on css3 performance and using webkits dev flags to debug GPU rendering.
From terminal (OS X), you can launch Safari with the GPU rendering debug flag with this:
CA_COLOR_OPAQUE=1 /Applications/Safari.app/Contents/MacOS/Safari
This will show you (in translucent red) which DOM regions are being rendered on the GPU and which ones are rendered by the CPU like this.
Related
We now all know, particularly from that nice article, that we should prefer css-transforms to animate position.
But we got the choice between translate() and translate3d()...
Which one is generally faster?
This site below runs tests comparing translate(), translate3d(), and a couple other properties. According to it, translate3d() is faster in most browsers.
http://jsperf.com/translate3d-vs-xy
The use of translate3d pushes CSS animations into hardware acceleration. Even if you're looking to do a basic 2d translation, use translate3d for more power! So 'T3d' is just better because it tells the CSS animations to push the animations in 3d power!
That's why it is faster.
(The answer of Cameron Little is the proof)
As Cameron suggested translate3d should be faster in a lot of browsers, mostly those that use GFX hardware acceleration to speed up rendering. Especially on WebKit translate3d was the preferred way of forcing hardware acceleration on page items.
Though in my experience sometimes there is one drawback to using 3d transforms - in certain browser versions/OS combinations (macOS + Safari I'm looking at you) hardware accelerated rendering can slightly alter font smoothing as well as interpolation, thus causing minor flicker or blur. Those situations can mostly be worked around but should be kept in mind.
On this html5rocks article, it states that
In general the CSS ‘opacity’ property isn’t hardware accelerated, but some browsers that implement filters using hardware acceleration will accelerate the filter version of opacity for much better performance.
This seems to imply that in performance-intensive applications, one should use the opacity filter instead of the opacity property. For example, I'm rendering a canvas under an image with an opacity property of 0.5. Should I be using the filter instead? How could one measure performance gains when using this filter property, and on what platforms might there be a noticeable improvement?
First, CSS3 filter effects are still a draft standard and their browser support varies. So think twice whether you really need them (e.g., CSS Filters in the Real World article (04.2013) reports rendering artifacts).
Hardware acceleration in Webkit and GPU Accelerated Compositing in Chrome give an overview of their implementations and both suggest there's no discrimination against CSS2 or CSS3 (I'd be surprised if there was).
Second, a review in PC magazine (01.2013) shows results of some online benchmarks by IE and Firefox teams. There are canvas, particle and CSS (rotation) benchmarks. The Maxthon (uses WebKit) and Opera versions tested there do not support acceleration so they give a hint on the performance gain. Regarding the transparency: it's orders of magnitude less computations than resampling so you shouldn't notice any difference for this specific operation.
Paul Irish says here that opacity is one of the few CSS properties that *is* GPU accelerated: https://plus.google.com/+AddyOsmani/posts/aTRerYcZpts
And also, there is a severe lack of support for filters across browsers as can be seen here, though opacity is supported across the board: http://caniuse.com/#search=opacity.
Not to mention the opacity property is just so much easier to use.
I'd stick with what you've got.
A number of blogs have expressed the performance gain in 'tricking' the GPU to think that an element is 3D by using transform: translateZ(0) to speed up animations and transitions. I was wondering if there are implications to using this transform in the following manner:
* {
-webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-ms-transform: translateZ(0);
-o-transform: translateZ(0);
transform: translateZ(0);
}
CSS transformations create a new stacking context and containing block, as described in the spec. In plain English, this means that fixed position elements with a transformation applied to them will act more like absolutely positioned elements, and z-index values are likely to get screwed with.
If you take a look at this demo, you'll see what I mean. The second div has a transformation applied to it, meaning that it creates a new stacking context, and the pseudo elements are stacked on top rather than below.
So basically, don't do that. Apply a 3D transformation only when you need the optimization. -webkit-font-smoothing: antialiased; is another way to tap into 3D acceleration without creating these problems, but it only works in Safari.
If you want implications, in some scenarios Google Chrome performance is horrible with hardware acceleration enabled. Oddly enough, changing the "trick" to -webkit-transform: rotateZ(360deg); worked just fine.
I don't believe we ever figured out why.
It forces the browser to use hardware acceleration to access the device’s graphical processing unit (GPU) to make pixels fly. Web applications, on the other hand, run in the context of the browser, which lets the software do most (if not all) of the rendering, resulting in less horsepower for transitions. But the Web has been catching up, and most browser vendors now provide graphical hardware acceleration by means of particular CSS rules.
Using -webkit-transform: translate3d(0,0,0); will kick the GPU into action for the CSS transitions, making them smoother (higher FPS).
Note: translate3d(0,0,0) does nothing in terms of what you see. It moves the object by 0px in x,y and z axis. It's only a technique to force the hardware acceleration.
Good read here: http://www.smashingmagazine.com/2012/06/21/play-with-hardware-accelerated-css/
I can attest to the fact that -webkit-transform: translate3d(0, 0, 0); will mess with the new position: -webkit-sticky; property. With a left drawer navigation pattern that I was working on, the hardware acceleration I wanted with the transform property was messing with the fixed positioning of my top nav bar. I turned off the transform and the positioning worked fine.
Luckily, I seem to have had hardware acceleration on already, because I had -webkit-font-smoothing: antialiased on the html element. I was testing this behavior in iOS7 and Android.
On mobile devices sending everything to the GPU will cause a memory overload and crash the application. I encountered this on an iPad app in Cordova. Best to only send the required items to the GPU, the divs that you're specifically moving around.
Better yet, use the 3d transitions transforms to do the animations like translateX(50px) as opposed to left:50px;
I'm currently working on a little site for my family. One of the things I wanted to do was to make a basic 'making of' stop-motion video. I could assemble it and upload it to Vimeo or something but I thought it was a perfect opportunity to use nothing but HTML, CSS, and Javascript.
I've got everything styled and my JS is working, etc. except that it performs atrociously in Chrome and Safari. Interestingly, it works great in Firefox and I'm not supporting it yet in IE. I'm hoping for 8 to 12 frames per second, with music playing, which I haven't bothered trying yet due to this. Bad performance is anything less than that. Currently I'm getting roughly 3 fps in Firefox (acceptable, but not what I was looking for) and in Chrome and Safari I'm getting roughly .6795 fps.
When running the Chrome Profiler, I get the following (relevant) output.
99.96% 99.96% (program)
0.03% 0.03% (garbage collector)
0.01% 0.01% script.js:5:nextSlide
I've never used the Profiler before but I believe this is showing me that my JS is not what's hitting the performance so hard.
I've published a test page that documents the performance differences that you can visit with Chrome and Firefox.
I've also discovered that this seems to be related to the images cycled. Cycling different, simpler images seems to work just fine in both Chrome and Firefox, despite the fact that Chrome is still a little more power hungry than Firefox.
As further proof of at least this conclusion, though it's entirely unacceptable, is demonstrated here, after running the images through convert -compress JPEG -quality 1. They cycle much more efficiently, but of course the quality is terrible.
I have run these test pages in Chrome (16.0.912.63), Safari (5.1.2 (6534.52.7)), WebKit nightly (Version 5.1.2 (6534.52.7, r102985)), and Mobile Safari (latest as of 2011/12/28) and only Mobile Safari performs as well as FireFox. The desktop browsers were tested on a MacBook Pro.
2.7 GHz Intel Core i7
8 GB 1333 MHz DDR3
Interestingly, Mobile Safari on an iPad 2 performs as well as FireFox when rendering the test page. Though Mobile Safari is based on WebKit, in this instance it performs entirely different.
Decreasing the setTimeout call to 144 from 244 also seems to not do anything. I've arrived at 244 entirely arbitrarily at this point as it became clear early on that the timing of the display compared to the call didn't seem to correspond nearly directly. This leads me to believe that I'm rendering the slide show as quickly as I can on each browser.
So my question is, how can I make this performant in WebKit?
You can debug the page performance in Chrome using the Timeline tab under the Chrome developer tools. The problem with your script is that your repaint cycle is simply too expensive, it currently takes 1.35s to repaint every frame.
The bad performance has nothing to do with the quality of the jpeg images (although the image quality also affects the page render time). The problem is that you are updating the z-index which causes the Chrome to repaint all images instead of just the next frame (You have a O(n) image slider website!).
The browsers try to do the minimal possible actions in response to a change e.g.: changes to an elements color will cause only repaint of the element.
Changing the element z-index property is basically the same as removing a node from the tree and adding another node to it. This will cause layout and repaint of the element, its children and possibly siblings. My guess is that in Chrome, the siblings are being repainted too, this explains the horrible performance.
A way to fix this problem is to update the opacity property instead of the z-index. Unlike the z-index, the opacity does not modifies the DOM tree. It only tells the render to ignore that element. The element is still 'physically' present in the DOM. That means that only one element gets repainted and not all siblings and children.
This simple changes in your CSS should do the trick:
.making-of .slide#slide-top {
opacity: 1;
/* z-index: 5000; */
}
.making-of .slide {
position: fixed;
/* z-index: 4000; */
opacity: 0;
....
}
And this is the result, the repaint went from 1.35s to 1ms:
EDIT:
Here is a jsfiddle using the opacity solution, I also added CSS3 transitions (just for fun!)
http://jsfiddle.net/KN7Y5/3/
More info on how the browser rendering works:
http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/
I took a look at the code on your site and found two things that are limiting the speed.
1) In the JavaScript, you have a timeout of approximately 1/4 second (244 milliseconds). This means that your best-cast frame-rate is about 4 FPS (frames-per-second). This can be fixed by simply reducing the delay to match the frame rate that you actually want. I see that your most recent edit addresses this point, but I didn't want to ignore it since it is ultimately critical to achieving the higher frame-rates that you want.
2) You are using z-index to control which image is visible. In the general case, z-index handling allows for objects that have different sizes and positions to be ordered so that you can control which object is visible at locations where two or more objects overlap. In your case, all of the objects overlap perfectly, and the z-index approach works fine except for one major problem: browsers don't optimize z-index processing for this case and therefore they are actually processing every image on every frame. I verified this by creating a modified version of your demo which used twice as many images -- the FPS was reduced by nearly a factor of 2 (in other words, it took 4 times as long to display the entire set).
I hacked together an alternative approach that achieved a much higher FPS (60 or more) under both Chrome and Firefox. The gist of it was that I used the display property instead of manipulating z-index:
.making-of .slide#other {
display: none;
}
.making-of .slide#slide-top {
display: inline;
}
and the JavaScript:
function nextSlide() {
...
topSlide.id='other';
nextTopSlide.id='slide-top';
...
setTimeout(nextSlide, 1);
...
}
I made some changes in the HTML too, notably including id="other" in the tag for each image.
So why is WebKit so slow? As has been pointed out in other comments, the extra-poor performance that you are seeing on Webkit seems to be Mac specific. My best guess about this is that the Mac version of WebKit is not actually using the "turbo" version of libjpeg (despite the fact that it is listed in the credits). In your test, JPEG decompression could very well be the gating factor if it is actually decompressing every image on every frame (as is likely the case). Benchmarking of libjpeg-turbo has shown about a 5x improvement in decompression speed. This roughly matches the difference that you are seeing between Firefox and Chrome (3 FPS vs. 0.6795 FPS).
For more notes on libjpeg-turbo and how this hypothesis explains some of your other results, see my other answer.
Key in my experience is to keep as less as possible images in the DOM and in javascript arrays, so don't load all of the at once, keep it to a minimum. Also make sure you destroyed already used DOM elements as well as javascript objects holding images, manual garbage collection. This will improve performance.
Random guess: GPU acceleration. It is device-dependent, and there is a big race among browsers now.
You could try with a more recent Chrome like the canaries, http://tools.google.com/dlpage/chromesxs (it's 18.x now), just to get more data.
about:version in Chrome should give you version of WebKit.
Also, have you tried existing slideshow solutions like http://jquery.malsup.com/cycle/ ? I wonder if playing with the z-index is the bottleneck here... maybe having only 1-2 images displayed (all the rest using display:none) would help. This is again a guess.
The best way to achieve better performance when it comes to graphics is to compress them, but like you want, but keep
If you are using Linux, I have used JPEG compression tool http://linuxpoison.blogspot.com/2011/01/utility-to-optimize-compress-jpeg-files.html before. It doesn't hurt quality as much as the ImageMagick example you gave.
Also http://trimage.org/ has JPG support, and would be my first recommendation!
If you are on Windows, maybe something like this:
http://www.trans4mind.com/personal_development/convertImage/index.html
I have not tested the Windows method, and I'm not even sure it supports batch
Hope that helps!
P.S. For PNGs I use sometimes use http://pmt.sourceforge.net/pngcrush/ along with or without http://trimage.org/
There has been some relatively recent work on the JPEG image compression library that is used in many applications including browsers such as Firefox and Chrome. This new library achieves a significant speed increase by using special media-processing instructions available in modern CPUs. It may simply be that your version of Chrome doesn't use the new library.
Your question requests a way to fix your images, but that shouldn't be necessary -- after all, some other browsers work fine. Therefore, the fix should be in the browser (and browsers are constantly being improved).
You said that you improved Chrome's speed by dramatically reducing the quality or complexity of your images. This could be explained by the fact that for areas of very low detail, the JPEG decompression algorithm can bypass a lot of the work that it would normally need to perform. If an 8x8 tile of pixels can be reduced to a single color, then decompression of that tile becomes a very simple matter.
This Wikipedia article provides some additional info and sources. It says that Chrome version 11 has the new library. You can enter "chrome://credits" in your location bar and see if it references "libjpeg-turbo". "libjpeg" is the original library and "libjpeg-turbo" is the optimized version.
One other possibility is that libjpeg-turbo isn't supported in Webkit on the Mac (although I don't specifically know that). There is a hint as to why that might be the case posted here.
P.S. You may get better decompression speed by compressing with a different algorithm, such as PNG (although your compression ratios will likely suffer). On the other hand, maybe you should use HTML5 video, probably with the WebM format.
I tested it in opera and it ran slow as hell, i noticed that opera had queued 150+ images to download it could be worth a try to download ~20 at a time?
An alternative approach would be to render this content as a video - it is ideal for this kind of thing and can easily contain audio and subtitles. You can access each pixel from each frame using JavaScript if you want to get funky.
I've been putting a page together today and thought that I'd implement a striped background using CSS3. All seemed fine in all browsers except for Firefox. (I'm using 5.01 but checked in 5.0 too)
The code is simple enough:
body {
background-color: #ebeced;
background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, .2) 25%,
transparent 25%,transparent 50%, rgba(255, 255, 255, .2) 50%,
rgba(255, 255, 255, .2) 75%,transparent 75%, transparent);
background-size:6px 6px;
}
Works great in Chrome and opera but it practically crashes FF.
Obviously I know that I can achieve this effect with a repeating background image but I was wondering if anyone else had a similar issue or is it just teething problems with CSS3 again ;)
Testing in jsfiddle produces the same issues, I didn't make a fiddle as I didn't want to be responsible for crashing you!
Oh and it's not a slow pc issue, I'm on an i7, 8gb ram, blah blah.
I believe it to be a problem with the background-size property as if you increase this or remove it entirely then the issue is resolved, however it's not suitable for me unless I can use background-size.
I am pretty sure I know what the problem is.
The performance problem is not very apparent for me, but I am on a new quad-core. What I do see is that when I create a blank page and put that css in there, then if I resize the browser, the redraw is a little clunky.
The problem comes from the fact that you are combining drawing CSS3 gradients with tiling that gradient at a very small size (6px). This means that for every 1000 sq.px of page real estate, the gradient will tile 27,777 times [1000/6 squared]. That is a ton! A smart browser would draw the gradient once and then tile it. It seems like Firefox may draw the gradient every time. If that is the case, that would indeed be a bug. If I modify the background size property to something larger, like 20px then I no longer see a performance problem, but others will. If you don't want your users experiencing these kinds of problems, I would say that you should stay away from this combination because slower computers will still have problems when you don't see any.
The easy fix is to not use the css3 gradients. Just create an image - browsers are good at tiling those. Even with images, it is sometimes better to use larger images ...
Early versions of Netscape Navigator used to ignore backgrounds if they were smaller than 40x40 pixels. I remember discovering that the cause of the bug I was trying to solve, and realizing that even though it seemed nice to be able to use a 1x20 image for a background because that would be the minimum file size, it was not the best choice for performance because it creates much more rendering work for the browser. Instead, I create a larger 50x20 version of the image. The effect is the same, but it saves some processing time.
Have you tried adding...
-moz-background-size:6px 6px;
...to your css?