diff --git a/README.md b/README.md
index 8c27522..14fa089 100644
--- a/README.md
+++ b/README.md
@@ -77,3 +77,9 @@ The row, counted from the end, that triggers the `scrollEnd` expression.
 Type: `number`. Default: `2`
 
 The number of excess rows that are added to the DOM. 
+
+#### debounce
+
+Type: `number`. Default: `0`
+
+The debounce in milliseconds. This will only affect the calls to `$digest`. The cells will still be moved smoothly. A value of `0` is interpreted as no debounce.
\ No newline at end of file
diff --git a/demo/index.html b/demo/index.html
index 6345013..d4496e5 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -45,6 +45,10 @@
           <label for="horizontal">Align Horizontal:</label>
           <input id="horizontal" class="form-control" type="checkbox" ng-model="options.alignHorizontal">
         </fieldset>
+        <fieldset class="form-group">
+          <label for="debounce">Debounce:</label>
+          <input id="debounce" class="form-control" type="number" ng-model="options.debounce">
+        </fieldset>
         <button type="button" class="btn btn-default" ng-click="fixSize()">Fix size</button>
         <button type="button" class="btn btn-default" ng-click="changeData()">Change Data</button>
         <button type="button" class="btn btn-default" ng-click="insert()">Insert</button>
diff --git a/dist/tileview.js b/dist/tileview.js
index b296bf0..b5dc2c9 100644
--- a/dist/tileview.js
+++ b/dist/tileview.js
@@ -31,8 +31,9 @@
      * - **scrollEndOffset** - {number} - Some features that rely on the `scrollEnd` callback need to be informed in advance.
      * This property specifies an offset in rows to trigger the scroll end event before actually hitting the bottom of the data. **Default**: 0
      * - **overflow** - {number} - Number of rows that are rendered additionally to the visible rows to make the scrolling experience more fluent. **Default**: 2
+     * - **debounce** - {number} - Debounce in milliseconds. This will only affect the calls to `$digest`. The cells will still be moved smoothly. A value of `0` is interpreted as no debounce. **Default**: 0.
        */
-    mod.directive('tdTileview', ['$compile', '$templateCache', '$window', function ($compile, $templateCache, $window) {
+    mod.directive('tdTileview', ['$compile', '$templateCache', '$timeout', '$window', function ($compile, $templateCache, $timeout, $window) {
             return {
                 restrict: 'E',
                 scope: {
@@ -60,6 +61,7 @@
                     scope.$watch('options', function (options) {
                         options.scrollEndOffset = def(options.scrollEndOffset, 0);
                         options.overflow = def(options.overflow, 2);
+                        options.debounce = def(options.debounce, 0);
                     });
                     scope.$watchGroup(['options.tileSize.width', 'options.tileSize.height'], function (_a) {
                         var width = _a[0], height = _a[1];
@@ -224,42 +226,46 @@
                             setPlaceholder();
                         }
                     }
-                    function forEachItem(startIndex, endIndex, fn) {
-                        var fromRight = startIndex > endIndex;
-                        var incr = fromRight ? -1 : 1;
-                        var j = fromRight ? -1 : 0;
-                        for (var i = startIndex; i < endIndex; i += incr) {
-                            fn(scope.items[i], j);
-                        }
+                    function updateAll() {
+                        forEachElement(function (el, i) { return updateItem(el, scope.items[startRow * itemsPerRow + i], true); });
                     }
+                    var debounceTimeout;
                     function onScroll() {
                         var oldStartRow = startRow;
                         var oldEndRow = endRow;
                         updateVisibleRows();
-                        if (startRow > oldEndRow || endRow < oldStartRow) {
-                            forEachElement(function (el, i) { return updateItem(el, scope.items[startRow * itemsPerRow + i], true); });
+                        if (scope.options.debounce !== undefined && scope.options.debounce > 0) {
+                            if (debounceTimeout) {
+                                $timeout.cancel(debounceTimeout);
+                            }
+                            debounceTimeout = $timeout(updateAll, scope.options.debounce);
                         }
                         else {
-                            var intersectionStart = Math.max(startRow, oldStartRow);
-                            var intersectionEnd = Math.min(endRow, oldEndRow);
-                            if (endRow > intersectionEnd) {
-                                var j = 0;
-                                for (var i = intersectionEnd * itemsPerRow; i < endRow * itemsPerRow; ++i) {
-                                    updateItem(itemContainer.children().eq(j++), scope.items[i], true);
-                                }
-                                for (var i = intersectionEnd * itemsPerRow; i < endRow * itemsPerRow; ++i) {
-                                    var itemElement = itemContainer.children().eq(0).detach();
-                                    itemContainer.append(itemElement);
-                                }
+                            if (startRow > oldEndRow || endRow < oldStartRow) {
+                                updateAll();
                             }
-                            else if (startRow < intersectionStart) {
-                                var j = -1;
-                                for (var i = intersectionStart * itemsPerRow - 1; i >= startRow * itemsPerRow; --i) {
-                                    updateItem(itemContainer.children().eq(j--), scope.items[i], true);
+                            else {
+                                var intersectionStart = Math.max(startRow, oldStartRow);
+                                var intersectionEnd = Math.min(endRow, oldEndRow);
+                                if (endRow > intersectionEnd) {
+                                    var j = 0;
+                                    for (var i = intersectionEnd * itemsPerRow; i < endRow * itemsPerRow; ++i) {
+                                        updateItem(itemContainer.children().eq(j++), scope.items[i], true);
+                                    }
+                                    for (var i = intersectionEnd * itemsPerRow; i < endRow * itemsPerRow; ++i) {
+                                        var itemElement = itemContainer.children().eq(0).detach();
+                                        itemContainer.append(itemElement);
+                                    }
                                 }
-                                for (var i = intersectionStart * itemsPerRow - 1; i >= startRow * itemsPerRow; --i) {
-                                    var itemElement = itemContainer.children().eq(-1).detach();
-                                    itemContainer.prepend(itemElement);
+                                else if (startRow < intersectionStart) {
+                                    var j = -1;
+                                    for (var i = intersectionStart * itemsPerRow - 1; i >= startRow * itemsPerRow; --i) {
+                                        updateItem(itemContainer.children().eq(j--), scope.items[i], true);
+                                    }
+                                    for (var i = intersectionStart * itemsPerRow - 1; i >= startRow * itemsPerRow; --i) {
+                                        var itemElement = itemContainer.children().eq(-1).detach();
+                                        itemContainer.prepend(itemElement);
+                                    }
                                 }
                             }
                         }
diff --git a/package.json b/package.json
index cf7b0e4..5039111 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "angular-tileview",
-  "version": "0.3.3",
+  "version": "0.5.0",
   "description": "A tileview for angular",
   "main": "gulpfile.js",
   "scripts": {
diff --git a/src/tileview.ts b/src/tileview.ts
index 956741b..8f49f59 100644
--- a/src/tileview.ts
+++ b/src/tileview.ts
@@ -36,8 +36,9 @@ declare const angular: any;
    * - **scrollEndOffset** - {number} - Some features that rely on the `scrollEnd` callback need to be informed in advance. 
    * This property specifies an offset in rows to trigger the scroll end event before actually hitting the bottom of the data. **Default**: 0
    * - **overflow** - {number} - Number of rows that are rendered additionally to the visible rows to make the scrolling experience more fluent. **Default**: 2
+   * - **debounce** - {number} - Debounce in milliseconds. This will only affect the calls to `$digest`. The cells will still be moved smoothly. A value of `0` is interpreted as no debounce. **Default**: 0.
 	 */
-  mod.directive('tdTileview', ['$compile', '$templateCache', '$window', ($compile, $templateCache, $window) => {
+  mod.directive('tdTileview', ['$compile', '$templateCache', '$timeout', '$window', ($compile, $templateCache, $timeout, $window) => {
     return {
       restrict: 'E',
       scope: {
@@ -72,6 +73,7 @@ declare const angular: any;
         scope.$watch('options', options => {
           options.scrollEndOffset = def(options.scrollEndOffset, 0);
           options.overflow = def(options.overflow, 2);
+          options.debounce = def(options.debounce, 0);
         });
         scope.$watchGroup(['options.tileSize.width', 'options.tileSize.height'], ([width, height]) => {
           layout();
@@ -256,7 +258,12 @@ declare const angular: any;
             setPlaceholder();
           }
         }
+        
+        function updateAll() {
+          forEachElement((el, i) => updateItem(el, scope.items[startRow * itemsPerRow + i], true));
+        }
 
+        let debounceTimeout;
         function onScroll() {
           
           const oldStartRow = startRow;
@@ -264,29 +271,36 @@ declare const angular: any;
           
           updateVisibleRows();
 
-          if (startRow > oldEndRow || endRow < oldStartRow) {
-            forEachElement((el, i) => updateItem(el, scope.items[startRow * itemsPerRow + i], true));
+          if (scope.options.debounce !== undefined && scope.options.debounce > 0) {
+            if (debounceTimeout) {
+              $timeout.cancel(debounceTimeout);
+            }
+            debounceTimeout = $timeout(updateAll, scope.options.debounce);
           } else {
-            const intersectionStart = Math.max(startRow, oldStartRow);
-            const intersectionEnd = Math.min(endRow, oldEndRow);
+            if (startRow > oldEndRow || endRow < oldStartRow) {
+              updateAll();
+            } else {
+              const intersectionStart = Math.max(startRow, oldStartRow);
+              const intersectionEnd = Math.min(endRow, oldEndRow);
 
-            if (endRow > intersectionEnd) {
-              let j = 0;
-              for (let i = intersectionEnd * itemsPerRow; i < endRow * itemsPerRow; ++i) {
-                updateItem(itemContainer.children().eq(j++), scope.items[i], true);
-              }
-              for (let i = intersectionEnd * itemsPerRow; i < endRow * itemsPerRow; ++i) {
-                const itemElement = itemContainer.children().eq(0).detach();                
-                itemContainer.append(itemElement);
-              }
-            } else if (startRow < intersectionStart) {
-              let j = -1;
-              for (let i = intersectionStart * itemsPerRow - 1; i >= startRow * itemsPerRow; --i) {
-                updateItem(itemContainer.children().eq(j--), scope.items[i], true);
-              }
-              for (let i = intersectionStart * itemsPerRow - 1; i >= startRow * itemsPerRow; --i) {
-                const itemElement = itemContainer.children().eq(-1).detach();
-                itemContainer.prepend(itemElement);
+              if (endRow > intersectionEnd) {
+                let j = 0;
+                for (let i = intersectionEnd * itemsPerRow; i < endRow * itemsPerRow; ++i) {
+                  updateItem(itemContainer.children().eq(j++), scope.items[i], true);
+                }
+                for (let i = intersectionEnd * itemsPerRow; i < endRow * itemsPerRow; ++i) {
+                  const itemElement = itemContainer.children().eq(0).detach();                
+                  itemContainer.append(itemElement);
+                }
+              } else if (startRow < intersectionStart) {
+                let j = -1;
+                for (let i = intersectionStart * itemsPerRow - 1; i >= startRow * itemsPerRow; --i) {
+                  updateItem(itemContainer.children().eq(j--), scope.items[i], true);
+                }
+                for (let i = intersectionStart * itemsPerRow - 1; i >= startRow * itemsPerRow; --i) {
+                  const itemElement = itemContainer.children().eq(-1).detach();
+                  itemContainer.prepend(itemElement);
+                }
               }
             }
           }