Last Updated: April 23, 2019
· irony

Parallax scroll with CSS

There are a few ways of doing a parallax scrolling effect. Most examples you find when searching on StackOverflow etc contains examples of moving individual layers or divs with jQuery triggered by the onscroll event on the document. This blog describes a more efficient way of solving this problem which means better performance, easier code to maintain and a more semantic meaning of the markup itself.


Working demo in webkit - the technology is supported in all major HTML5 capable browsers (IE10+, Safari, Chrome and Firefox). The demo is right now optimized for Webkit but I plan to make it work on all the other browsers too:


But first, some words about Parallax Scroll

Parallax Scroll is an old technique to simulate depth in view in computer games and have been popular ever since 1982 when the first game Moon Patrol introduced it to games. The basic idea is that objects that are farther away move slower in relation to objects in the front and by mimicking this effect we get a sense of 3D on our 2D screen.

HTML5 and flash

Parallax scroll have been used in the web for quite a few years and Iteams old site released 2010 was based solely on this effect. A few years back we couldn't simulate this effect very well with standard HTML and needed a lot of tricks like setting background position and fallback to flash etc.

Welcome CSS3 and TranslateZ

Now we have powerful tools built in to CSS3 which enables us to talk to the GPU directly and instead of using Javascript to control the placement of our elements individually we can move them all together meaning better performance and open up new possibilities.

The mobile problem

Unfortunately there are no good ways yet to reproduce the effect on a mobile device yet and the reason for that is that the scroll events only triggers once the scroll has stopped, in the meantime the scrolling only occurs by moving a bitmap by the GPU. If anyone know any tricks, I will update this blog - it would be awesome to also support mobile in a semantic way.

The solution

The basic trick is to make each element positioned with a correct Z position relative to your normal depth which is 0,

translateZ: -30px

Apply some perspective to your body element

   -webkit-perspective: 500;
   -webkit-perspective-origin: 0 0;

Now you just update the perspective origin whenever the user scrolls the page. This means all elements will move individually by the GPU and no CPU cycles will be wasted to keep track on them. Since you are actually moving the perspective you can also use this to show real 3D objects like graphs, pins on a map, shadows, reflections or other cool things you could come up with.

After you have applied correct depth to your elements, just add this row to readjust the perspective:

    window.onscroll = function(){ =
       window.scrollX + "px " + 
       window.scrollY + "px";

7 Responses
Add your response

This is excellent - I've been looking for something like this!

Would it be possible to use the "touchstart" and "touchmove" events, some direction detection logic, and CSS3 transitions+easings in combination with your solution to approximate "inertia parallax"?

over 1 year ago ·

Good idea. I'll look into that!

over 1 year ago ·

On Android it actually works already!

over 1 year ago ·

Great posting. Its a great way to build single page apps.

over 1 year ago ·

You are a genius!

over 1 year ago ·

Thank you very much, this is just what I have been looking for!!

Skrollr is great, but a right pain to implement, update and make mobile ready.
But this method is great for a light-weight and easy to use parallaxing effect to add depth to your website.

Thanks again!! :)

over 1 year ago ·

There's also the option to make the perspective container scrolling, and drop the JS. Tadaa: instant parallax. Because the content moves in a scrolling div, you don't need to adjust the perspective origin. Neat trick, right?

Unfortunately, you'll lose elastic scrolling on mobile, and that is quite a deal breaker.

For now, onscroll + touchmove appears to be the best solution.


See for the pure CSS solution.

over 1 year ago ·