Backbone vs. Ember vs. React: A Benchmark

by blesh

HTML

<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="http://backbonejs.org/backbone-min.js"></script>
<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.3.0.js"></script>
<script src="https://rawgithub.com/ebryn/0bbe6639da1166bef3ee/raw/c5f715bbee4200d328065fdb34b0bb49f2d748cf/ember-htmlbars-dont-use-me-anywhere-else.prod.js"></script>
<script src="http://react.zpao.com/builds/master/latest/react.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>

<p>
    This is a fork of <a href="http://jsfiddle.net/jashkenas/CGSd5/" target="_top">jashkenas's Ember/Backbone</a> benchmark to include React. Note that this is just a simple benchmark: you wouldn't be doing this in an actual app.
</p>

<button onclick="runBackbone()">Animate with Backbone</button>
<button onclick="runEmber()">Animate with Ember+HTMLBars</button>
<button onclick="runReact()">Animate with React</button>
<button onclick="runRawdog()">Animate with nothing</button>
<button onclick="runAngular()">Animate with Angular</button>


<p id="timing">&nbsp;</p>
<div id="grid"></div>

<script type="x-html-in-html-template-in-template-weirdness" id="angular-template">
    <div class="toControl">
        <div class="box-view" ng-repeat="item in items track by $index">
            <div class="box" style="{{item.style}}">{{item.content}}</div>
        </div>
    </div>
</script>

<script type="x-template" id="underscore-template">
  <div class="box" id="box-<%= number %>" style="top: <%= top %>px; left: <%= left %>px; background: rgb(0,0,<%= color %>);">
    <%= content %>       
  </div>
</script>
        
<script type="text/x-htmlbars" id="htmlbars-box">
    <div class="box" id="{{number}}" style="{{style}}"> {{content}}</div>
</script>

CSS

p {
  font: 12px/16px Arial;
  margin: 10px 10px 15px;    
}

button {
  font: bold 14px/14px Arial;  
  margin-left: 10px;
}

#grid {
  margin: 10px;
}

#timing {
  clear: both;
  padding-top: 10px;
}

.box-view {
  width: 20px; height: 20px;
  float: left;
  position: relative;
  margin: 8px;    
}

.box {
  border-radius: 100px;
  width: 20px; height: 10px;
  padding: 5px 0;
  color: #fff;
  font: 10px/10px Arial;
  text-align: center;
  position: absolute;
}

JavaScript

// Change N to change the number of drawn circles.

var N = 100;

// The Angular implementation: 
(function (){

    var app = angular.module('animateApp', []);
    var angularAnimate;
    
    app.controller('MainCtrl', function($scope) {
        var ctrl = this;
        ctrl.count = 0;
        $scope.items = [];
        for(var i = 0; i < N; i++) {
            $scope.items.push({});
        }
        
        angularAnimate = function() {
            $scope.$apply(function (){
                var count = ctrl.count += 1;
                angular.forEach($scope.items, function(item) {
                    item.top = Math.sin(count / 10) * 10;
                    item.left = Math.cos(count / 10) * 10;
                    item.color = count % 255;
                    item.content = count % 100;
                    item.style = 'background-color:rgb(0,0,' + item.color +');left:' + item.left + 'px;top:' + item.top +'px';
                });
            });
        };      
        
        console.log('controller instantiated');
    });

    var angularInit = function() {
        //This is a really weird way to test anything, but I'm game.
        var html = $('#angular-template').html();
        var el = angular.element(html);
        var grid = angular.element('#grid');
        grid.append(el);
        el.attr('ng-controller', 'MainCtrl');
        angular.bootstrap(grid, ['animateApp']);
    };
        
    window.runAngular = function() {
        reset();
        angularInit();
        console.log('benchmark start');
        benchmarkLoop(angularAnimate);
    };    
}());

// The Backbone implementation:
(function(){

var Box = Backbone.Model.extend({

    defaults: {
        top: 0,
        left: 0,
        color: 0,
        content: 0
    },

    initialize: function() {
        this.count = 0;
    },

    tick: function() {
        var count = this.count += 1;
        this.set({
            top: Math.sin(count / 10) * 10,
            left: Math.cos(count / 10) *...