How to make your next web application appear janky and clumsy

Slow down
  • David Rajchenbach-Teller
  • Mozilla
  • Firefox Performance Team
  • Zedge Tech Talks
  • Trondheim, May 8th, 2014

About Mozilla

Mozilla is my Dinosaur
  • Non-profit
  • ~1000 paid staff
  • ~2000 regular contributors
  • Firefox, Firefox OS
  • Protecting privacy
  • Teaching stuff that matters
Image by Sean Martell

So you want to go fast?

Going (too) fast Image by Fabulous Wallpapers

About jank

16ms- (sustained)      Smooth
40ms+ (sustained) Choppy
50ms+ (single) Jank
100ms+ (single) Broken
10s+ (single) Stopped responding

1. Know your enemy

A snail Image by palomaironique

This is a browser

      while (true) {
        layout.update();
        screen.paint();
        os.handleEvents();
        dom.handleEvents();
      }
      

2. Do too much too often

A busy mom Image by Gerald G

Setup

      requestAnimationFrame(function loop() {
        // ...
        // Insert something slow here.
        // ...
        requestAnimationFrame(loop);
      });
      

Running to completion

      requestAnimationFrame(function loop() {
        for (var i = 0; i < 10000; ++i) {
          doSomething();
        }
        requestAnimationFrame(loop);
      });
      

Beyond 60fps... not

      var element = document.getElementById("myDiv");
       
      window.addEventListener("mousemove", function(event) {
        element.style.left = event.pageX + "px";
        element.stlye.top = event.pageY + "py";
      });
      

Variants

What else can slow down the loop?
  • a call to alert()?
  • synchronous I/O (network, disk, GPU)?
  • allocating memory?
  • thrashing the layout?

3. Layout and Multiply

ussr calculator Image by molumen

Thrashing layout

      var el1 = document.getElementById("div1");
      var el2 = document.getElementById("div2");
       
      requestAnimationFrame(function loop() {
        el2.style.left = el1.offsetLeft + 5 + "px"; // Almost
        el2.style.top  = el1.offsetTop  + 5 + "px";
       
        requestAnimationFrame(loop);
      });
      

4. Enter the mind of the renderer

A Penguin at the movies Image by Moini

Reinventing scrolling

      var element = document.getElementById("foo");
      var i = 0;
       
      requestAnimationFrame(function loop() {
        element.stype.top = i++ * 5 + "px";
       
        requestAnimationFrame(loop);
      });
      

Reinterpreting layers

      <div style="transition:...">
        <span style="z-index:1"></span>
        <span style="z-index:3"></span>
      </div>
      <div style="z-index:2"></div>
      

Forcing large repaints

      <div id="el1" style="position:absolute;top:0;left:0">
      </div>
      <div id="el2" style="margin-left:90%;margin-top:90%">
      </div>
       
      el1.textContent = 'foo';
      el2.textContent = 'bar';
      

5. Waiting is an art

Le Penseur Source Wikipedia

Synchronous disk I/O

      
  • var item = localStorage.getItem("foo"); // Yes, it's that simple
  • var data = JSON.stringify(item); // Secondary cost

I/O in layout

      var context = document.getElementById("canvas").
        getContext("2d");
       
      requestAnimationFrame(function loop() {
        var image = new Image();
        image.src = "http://...";
        context.drawImage(image, 0, 0);
       
        requestAnimationFrame(loop);
      });
      

6. Want some more?

Banana Skin Author dominiquechappard

More

  • multiplying requests to resources;
  • waiting for too many resources;
  • hoarding resources (nodes, frames, strings, data);
  • etc.

7. The Future is Coming

A Centaur Image by tzunghaor

The future

  • Promise + Generators;
  • Workers for all;
  • Sandboxed iframes for all;
  • Servo?

Promise + Generators

      Task.async(function*() { 
        for (var i = 0; i < 10000; ++i) {
          yield doSomething();
        }
      });
      

Working with workers (maybe)

      myElement.addEventListener("mousemove", e => {
        var msg = {
          id: myElement.id,
          event: "mousemove",
          buttons: e.buttons
        };
        myWorker.postMessage(msg);
      });
      

...Companion worker

      self.addEventListener("message", {data} => {
        // ... 
        var msg = // ...
        self.postMessage(msg); 
      );
      

Where do we go from here?

A robot Image by bastiyxc

How to jank

  1. Know the browser.
  2. Key techniques: act synchronously, de-optimize.
  3. Key tools: profiler, timeline, the box repainter, pagespeed.
  4. Many frameworks will do jank for you.
  5. Resources: jankfree.org, irc.mozilla.org, ask.mozilla.org.
  6. Hint: an open-source browser is only as good as its contributors.

Thanks

Red panda (Firefox) Photo by Yortw