diff --git a/src/canvas.js b/src/canvas.js
index 9b3ed0ba9..c1d2785e8 100644
--- a/src/canvas.js
+++ b/src/canvas.js
@@ -59,15 +59,121 @@ function ScratchCanvas(width, height) {
   return canvas;
 }
 
+function addContextCurrentTransform(ctx) {
+  // If the context doesn't expose a `mozCurrentTransform`, add a JS based on.
+  if (!ctx.mozCurrentTransform) {
+    // Store the original context
+    ctx._originalSave = ctx.save;
+    ctx._originalRestore = ctx.restore;
+    ctx._originalRotate = ctx.rotate;
+    ctx._originalScale = ctx.scale;
+    ctx._originalTranslate = ctx.translate;
+    ctx._originalTransform = ctx.transform;
+
+    ctx._transformMatrix = [1, 0, 0, 1, 0, 0];
+    ctx._transformStack = [];
+
+    Object.defineProperty(ctx, 'mozCurrentTransform', {
+      get: function getCurrentTransform() {
+        return this._transformMatrix;
+      }
+    });
+
+    Object.defineProperty(ctx, 'mozCurrentTransformInverse', {
+      get: function getCurrentTransformInverse() {
+        // Calculation done using WolframAlpha:
+        // http://www.wolframalpha.com/input/?
+        //   i=Inverse+{{a%2C+c%2C+e}%2C+{b%2C+d%2C+f}%2C+{0%2C+0%2C+1}}
+
+        var m = this._transformMatrix;
+        var a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];
+
+        var ad_bc = a * d - b * c;
+        var bc_ad = b * c - a * d;
+
+        return [
+          d / ad_bc,
+          b / bc_ad,
+          c / bc_ad,
+          a / ad_bc,
+          (d * e - c * f) / bc_ad,
+          (b * e - a * f) / ad_bc
+        ];
+      }
+    });
+
+    ctx.save = function ctxSave() {
+      var old = this._transformMatrix;
+      this._transformStack.push(old);
+      this._transformMatrix = old.slice(0, 6);
+
+      this._originalSave();
+    };
+
+    ctx.restore = function ctxRestore() {
+      var prev = this._transformStack.pop();
+      if (prev) {
+        this._transformMatrix = prev;
+        this._originalRestore();
+      }
+    };
+
+    ctx.translate = function ctxTranslate(x, y) {
+      var m = this._transformMatrix;
+      m[4] = m[0] * x + m[2] * y + m[4];
+      m[5] = m[1] * x + m[3] * y + m[5];
+
+      this._originalTranslate(x, y);
+    };
+
+    ctx.scale = function ctxScale(x, y) {
+      var m = this._transformMatrix;
+      m[0] = m[0] * x;
+      m[1] = m[1] * x;
+      m[2] = m[2] * y;
+      m[3] = m[3] * y;
+
+      this._originalScale(x, y);
+    };
+
+    ctx.transform = function ctxTransform(a, b, c, d, e, f) {
+      var m = this._transformMatrix;
+      this._transformMatrix = [
+        m[0] * a + m[2] * b,
+        m[1] * a + m[3] * b,
+        m[0] * c + m[2] * d,
+        m[1] * c + m[3] * d,
+        m[0] * e + m[2] * f + m[4],
+        m[1] * e + m[3] * f + m[5]
+      ];
+
+      ctx._originalTransform(a, b, c, d, e, f);
+    };
+
+    ctx.rotate = function ctxRotate(angle) {
+      var cosValue = Math.cos(angle);
+      var sinValue = Math.sin(angle);
+
+      var m = this._transformMatrix;
+      this._transformMatrix = [
+        m[0] * cosValue + m[2] * sinValue,
+        m[1] * cosValue + m[3] * sinValue,
+        m[0] * (-sinValue) + m[2] * cosValue,
+        m[1] * (-sinValue) + m[3] * cosValue,
+        m[4],
+        m[5]
+      ];
+
+      this._originalRotate(angle);
+    };
+  }
+}
+
 var CanvasGraphics = (function canvasGraphics() {
   // Defines the time the executeIRQueue is going to be executing
   // before it stops and shedules a continue of execution.
   var kExecutionTime = 50;
 
-  // Number of IR commands to execute before checking
-  // if we execute longer then `kExecutionTime`.
-  var kExecutionTimeCheck = 500;
-
   function constructor(canvasCtx, objs) {
     this.ctx = canvasCtx;
     this.current = new CanvasExtraState();
@@ -77,6 +183,10 @@ var CanvasGraphics = (function canvasGraphics() {
     this.xobjs = null;
     this.ScratchCanvas = ScratchCanvas;
     this.objs = objs;
+
+    if (canvasCtx) {
+      addContextCurrentTransform(canvasCtx);
+    }
   }
 
   var LINE_CAP_STYLES = ['butt', 'round', 'square'];
@@ -112,31 +222,33 @@ var CanvasGraphics = (function canvasGraphics() {
       var i = executionStartIdx || 0;
       var argsArrayLen = argsArray.length;
 
+      // Sometimes the IRQueue to execute is empty.
+      if (argsArrayLen == i) {
+        return i;
+      }
+
       var executionEndIdx;
       var startTime = Date.now();
 
       var objs = this.objs;
 
-      do {
-        executionEndIdx = Math.min(argsArrayLen, i + kExecutionTimeCheck);
+      while (true) {
+        if (fnArray[i] !== 'dependency') {
+          this[fnArray[i]].apply(this, argsArray[i]);
+        } else {
+          var deps = argsArray[i];
+          for (var n = 0, nn = deps.length; n < nn; n++) {
+            var depObjId = deps[n];
 
-        for (i; i < executionEndIdx; i++) {
-          if (fnArray[i] !== 'dependency') {
-            this[fnArray[i]].apply(this, argsArray[i]);
-          } else {
-            var deps = argsArray[i];
-            for (var n = 0, nn = deps.length; n < nn; n++) {
-              var depObjId = deps[n];
-
-              // If the promise isn't resolved yet, add the continueCallback
-              // to the promise and bail out.
-              if (!objs.isResolved(depObjId)) {
-                objs.get(depObjId, continueCallback);
-                return i;
-              }
+            // If the promise isn't resolved yet, add the continueCallback
+            // to the promise and bail out.
+            if (!objs.isResolved(depObjId)) {
+              objs.get(depObjId, continueCallback);
+              return i;
             }
           }
         }
+        i++;
 
         // If the entire IRQueue was executed, stop as were done.
         if (i == argsArrayLen) {
@@ -153,7 +265,7 @@ var CanvasGraphics = (function canvasGraphics() {
 
         // If the IRQueue isn't executed completly yet OR the execution time
         // was short enough, do another execution round.
-      } while (true);
+      }
     },
 
     endDrawing: function canvasGraphicsEndDrawing() {
diff --git a/web/viewer.js b/web/viewer.js
index 00de7feaf..221c535ee 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -218,6 +218,13 @@ var PDFView = {
   },
 
   load: function pdfViewLoad(data, scale) {
+    function bindOnAfterDraw(pageView, thumbnailView) {
+      // when page is painted, using the image as thumbnail base
+      pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
+        thumbnailView.setImage(pageView.canvas);
+      };
+    }
+
     var loadingIndicator = document.getElementById('loading');
     loadingIndicator.setAttribute('hidden', 'true');
 
@@ -244,10 +251,14 @@ var PDFView = {
     var thumbnails = this.thumbnails = [];
     for (var i = 1; i <= pagesCount; i++) {
       var page = pdf.getPage(i);
-      pages.push(new PageView(container, page, i, page.width, page.height,
-                              page.stats, this.navigateTo.bind(this)));
-      thumbnails.push(new ThumbnailView(sidebar, page, i,
-                                        page.width / page.height));
+      var pageView = new PageView(container, page, i, page.width, page.height,
+                                  page.stats, this.navigateTo.bind(this));
+      var thumbnailView = new ThumbnailView(sidebar, page, i,
+                                            page.width / page.height);
+      bindOnAfterDraw(pageView, thumbnailView);
+
+      pages.push(pageView);
+      thumbnails.push(thumbnailView);
       var pageRef = page.ref;
       pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
     }
@@ -385,6 +396,8 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
     while (div.hasChildNodes())
       div.removeChild(div.lastChild);
     div.removeAttribute('data-loaded');
+
+    delete this.canvas;
   };
 
   function setupLinks(content, scale) {
@@ -499,6 +512,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
     canvas.id = 'page' + this.id;
     canvas.mozOpaque = true;
     div.appendChild(canvas);
+    this.canvas = canvas;
 
     var scale = this.scale;
     canvas.width = pageWidth * scale;
@@ -512,9 +526,16 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
     ctx.translate(-this.x * scale, -this.y * scale);
 
     stats.begin = Date.now();
-    this.content.startRendering(ctx, this.updateStats, function(e) {
-      PDFView.error('An error occured while rendering the page.', e);
-    });
+    this.content.startRendering(ctx, 
+      (function pageViewDrawCallback() {
+        this.updateStats();
+        if (this.onAfterDraw)
+          this.onAfterDraw();
+      }).bind(this),
+      function pageViewErrorback(e) {
+        PDFView.error('An error occured while rendering the page.', e);
+      }
+    );
 
     setupLinks(this.content, this.scale);
     div.setAttribute('data-loaded', true);
@@ -545,10 +566,9 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
   anchor.appendChild(div);
   container.appendChild(anchor);
 
-  this.draw = function thumbnailViewDraw() {
-    if (div.hasChildNodes())
-      return;
+  this.hasImage = false;
 
+  function getPageDrawContext() {
     var canvas = document.createElement('canvas');
     canvas.id = 'thumbnail' + id;
     canvas.mozOpaque = true;
@@ -576,7 +596,28 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
     div.style.height = (view.height * scaleY) + 'px';
     div.style.lineHeight = (view.height * scaleY) + 'px';
 
+    return ctx;
+  }
+
+  this.draw = function thumbnailViewDraw() {
+    if (this.hasImage)
+      return;
+
+    var ctx = getPageDrawContext();
     page.startRendering(ctx, function thumbnailViewDrawStartRendering() {});
+
+    this.hasImage = true;
+  };
+
+  this.setImage = function thumbnailViewSetImage(img) {
+    if (this.hasImage)
+      return;
+
+    var ctx = getPageDrawContext();
+    ctx.drawImage(img, 0, 0, img.width, img.height,
+                  0, 0, ctx.canvas.width, ctx.canvas.height);
+
+    this.hasImage = true;
   };
 };
 
@@ -714,6 +755,10 @@ window.addEventListener('transitionend', function webViewerTransitionend(evt) {
 
   var container = document.getElementById('sidebarView');
   container._interval = window.setInterval(function interval() {
+    // skipping the thumbnails with set images
+    while (pageIndex < pagesCount && PDFView.thumbnails[pageIndex].hasImage)
+      pageIndex++;
+
     if (pageIndex >= pagesCount) {
       window.clearInterval(container._interval);
       return;