title: Smoothly animating a large image in CSS using translate3d
body: <p>The following question is about using GPU accelerated rendering on large images to bust jank.</p>
  
  <p>I'm having trouble with making a large image, animated with translate3d, draw using the compositor thread.
  I've reduced the problem to this fiddle: <a href="https://jsfiddle.net/18c5o2pv/" rel="nofollow">https://jsfiddle.net/18c5o2pv/</a></p>
  
  <pre><code>&lt;html&gt;
  
  &lt;head&gt;
  
  &lt;style&gt;
  
  div {
      position:relative;
      width:1500px;
      overflow:hidden;
      border:1px solid;
      height:800px;
  }
  
  img {
      width:100%;
      position:absolute;
      top:0px;
      -webkit-animation: SJBGFore1 10s infinite linear;
  }
  @-webkit-keyframes SJBGFore1 {
      0%  {-webkit-transform: translate(0%, 0em);}
      100%  {-webkit-transform: translate(-100%, 0em);}
  }
  
  &lt;/style&gt;
  
  &lt;/head&gt;
  
  &lt;body&gt;&lt;div&gt;
      &lt;img src="http://www.storyjam.co.uk/fiddleresources/ground.svg"&gt;
  &lt;/div&gt;&lt;/body&gt;
  
  &lt;/html&gt;
  </code></pre>
  
  <p>Using Firefox Developer edition, I can see when Firefox is running the animation on the compositor thread. When the image is a couple of hundred pixels wide it works as desired. The image needs to be pretty large, though, since it's an animated background image.</p>
  
  <p>Finding that smaller images are composited correctly, I tried splitting the animated image into 7 chunks which animate in sequence: <a href="https://jsfiddle.net/2skpfn9s/" rel="nofollow">https://jsfiddle.net/2skpfn9s/</a>
  Firefox DE now runs them all on the compositor thread. Hurrah!</p>
  
  <pre><code>&lt;html&gt;
  
  &lt;head&gt;
  
  &lt;style&gt;
  
  div {
      position:relative;
      width:400px;
      overflow:hidden;
      border:1px solid;
      height:800px;
  }
  
  
  img {
      width:100%;
      position:absolute;
      top:0px;
  }
  
  img:nth-of-type(1){-webkit-animation: SJBGFore1 10s infinite linear;}
  @-webkit-keyframes SJBGFore1 {
      0%  {-webkit-transform: translate3d(0%, 0em, 0em);}
      10%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
      100%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
  }
  
  img:nth-of-type(2){-webkit-animation: SJBGFore2 10s infinite linear;}
  @-webkit-keyframes SJBGFore2 {
      0%  {-webkit-transform: translate3d(100%, 0em, 0em);}
      10%  {-webkit-transform: translate3d(0%, 0em, 0em);}
      20%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
      100%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
  }
  
  img:nth-of-type(3){-webkit-animation: SJBGFore3 10s infinite linear;}
  @-webkit-keyframes SJBGFore3 {
      0%  {-webkit-transform: translate3d(100%, 0em, 0em);}
      10%  {-webkit-transform: translate3d(100%, 0em, 0em);}
      20%  {-webkit-transform: translate3d(0%, 0em, 0em);}
      30%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
      100%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
  }
  
  img:nth-of-type(4){-webkit-animation: SJBGFore4 10s infinite linear;}
  @-webkit-keyframes SJBGFore4 {
      0%  {-webkit-transform: translate3d(100%, 0em, 0em);}
      20%  {-webkit-transform: translate3d(100%, 0em, 0em);}
      30%  {-webkit-transform: translate3d(0%, 0em, 0em);}
      40%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
      100%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
  }
  
  img:nth-of-type(5){-webkit-animation: SJBGFore5 10s infinite linear;}
  @-webkit-keyframes SJBGFore5 {
      0%  {-webkit-transform: translate3d(100%, 0em, 0em);}
      30%  {-webkit-transform: translate3d(100%, 0em, 0em);}
      40%  {-webkit-transform: translate3d(0%, 0em, 0em);}
      50%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
      100%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
  }
  
  img:nth-of-type(6){-webkit-animation: SJBGFore6 10s infinite linear;}
  @-webkit-keyframes SJBGFore6 {
      0%  {-webkit-transform: translate3d(100%, 0em, 0em);}
      40%  {-webkit-transform: translate3d(100%, 0em, 0em);}
      50%  {-webkit-transform: translate3d(0%, 0em, 0em);}
      60%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
      100%  {-webkit-transform: translate3d(-100%, 0em, 0em);}
  }
  
  img:nth-of-type(7){-webkit-animation: SJBGFore7 10s infinite ease-out;}
  @-webkit-keyframes SJBGFore7 {
      0%  {-webkit-transform: translate3d(100%, 0em, 0em);}
      50%  {-webkit-transform: translate3d(100%, 0em, 0em);}
      60%  {-webkit-transform: translate3d(0%, 0em, 0em);}
      70%  {-webkit-transform: translate3d(0%, 0em, 0em);}
      100%  {-webkit-transform: translate3d(0%, 0em, 0em);}
  }
  
  &lt;/style&gt;
  
  &lt;/head&gt;
  
  &lt;body&gt;&lt;div&gt;
      &lt;img src="http://www.storyjam.co.uk/fiddleresources/ground1.svg"&gt;
      &lt;img src="http://www.storyjam.co.uk/fiddleresources/ground2.svg"&gt;
      &lt;img src="http://www.storyjam.co.uk/fiddleresources/ground3.svg"&gt;
      &lt;img src="http://www.storyjam.co.uk/fiddleresources/ground4.svg"&gt;
      &lt;img src="http://www.storyjam.co.uk/fiddleresources/ground5.svg"&gt;
      &lt;img src="http://www.storyjam.co.uk/fiddleresources/ground6.svg"&gt;
      &lt;img src="http://www.storyjam.co.uk/fiddleresources/ground7.svg"&gt;
  &lt;/div&gt;&lt;/body&gt;
  
  &lt;/html&gt;
  </code></pre>
  
  <p>However, I can't replicate the improved performance in Chrome. No matter what I do, I get lots of janky frames, and this sequence of events in each frame, which causes a whole lotta jank:
  (Recalculate Style -> Update Layer Tree -> Update Layer Tree -> Composite Layers)</p>
  
  <p>It's confusing: Chrome is doing a 'Compositing Layers' action, which would suggest that it's using GPU acceleration. In that case, why is it having to recalculate style, why is it Updating the layer tree, and why am I get such nasty jank?</p>
  
  <p>THINGS I'VE TRIED:</p>
  
  <ol>
  <li>Animating it with 'left' to avoid GPU acceleration at all. Leads up a much worse performance.</li>
  <li>Using 'translate' instead of 'translate3d'. Makes no noticeable difference to process/performance.</li>
  </ol>