define([
    'knockout',
    'jquery',
    'lodash'
], function (ko, $, _) {
    function Canvas() {
        this.users = ko.observableArray();
        this.albums = ko.observableArray();
        this.items = ko.computed(_.bind(function () {
            return [].concat(this.users()).concat(this.albums());
        }, this));
        this.left = ko.observable(0);
        this.top = ko.observable(0);
        this.damping = ko.observable(0.5);
        this.attraction = ko.observable(0.01);
        this.repulsion = ko.observable(10);
        this.displacement = ko.observable(0);
        this.damping.subscribe(this.relayout, this);
        this.attraction.subscribe(this.relayout, this);
        this.repulsion.subscribe(this.relayout, this);
    }

    Canvas.prototype.clear = function () {
        this.users.removeAll();
        this.albums.removeAll();
    };

    Canvas.prototype.addUsers = function (users) {
        _.forEach(users, function (user) {
            user.displayed = true;
            user.loaded.subscribe(this.relayout, this);
        }, this);
        this.users(this.users().concat(users));
        this.relayout();
    };

    Canvas.prototype.addAlbums = function (albums) {
        _.forEach(albums, function (album) {
            album.displayed = true;
            album.loaded.subscribe(this.relayout, this);
        }, this);
        this.albums(this.albums().concat(albums));
        this.relayout();
    };

    Canvas.prototype.relayout = function () {
        if (this.interval) {
            return;
        }

        this.interval = window.setInterval(_.bind(function () {
            var items = this.items();

            var repulsion = this.repulsion();
            var attraction = this.attraction();
            var damping = this.damping();

            var dsq, coul;
            _.forEach(items, function (item1) {
                item1.force.x = item1.force.y = 0;
                _(items).without(item1).forEach(function (item2) {
                    dsq = (item1.pos.x - item2.pos.x) * (item1.pos.x - item2.pos.x) + (item1.pos.y - item2.pos.y) * (item1.pos.y - item2.pos.y);
                    if (dsq === 0) { dsq = 0.001; }
                    coul = repulsion / dsq;
                    item1.force.x += coul * (item1.pos.x - item2.pos.x);
                    item1.force.y += coul * (item1.pos.y - item2.pos.y);
                });
            });

            _.forEach(items, function (item1) {
                _.forEach(item1.related(), function (item2) {
                    if (item2.displayed) {
                        item1.force.x += attraction * (item2.pos.x - item1.pos.x);
                        item1.force.y += attraction * (item2.pos.y - item1.pos.y);
                        item2.force.x += attraction * (item1.pos.x - item2.pos.x);
                        item2.force.y += attraction * (item1.pos.y - item2.pos.y);
                    }
                });
            });


            var displacement = 0;
            _.forEach(items, function (item) {
                item.velocity.x = (item.velocity.x + item.force.x) * damping;
                item.velocity.y = (item.velocity.y + item.force.y) * damping;
                displacement += Math.abs(item.velocity.x) + Math.abs(item.velocity.y);
                item.pos.x += item.velocity.x;
                item.pos.y += item.velocity.y;
                item.position(item.pos);
            });

            if (displacement < 0.05 * items.length) {
                window.clearInterval(this.interval);
                this.interval = 0;
            }

            this.displacement(displacement);
        }, this), 1000 / 10);
    };

    Canvas.prototype.onMouseWheel = function (event) {
        this.left(this.left() - event.originalEvent.deltaX);
        this.top(this.top() - event.originalEvent.deltaY);
        event.preventDefault();
    };

    return Canvas;
});

@@ -952,24 +952,63 @@
 on (user) %7B%0A
+            if (!user.displayed) %7B%0A    
             
@@ -1022,32 +1022,36 @@
 splayed = true;%0A
+    
             user
@@ -1082,32 +1082,46 @@
 elayout, this);%0A
+            %7D%0A
         %7D, this)
@@ -1141,16 +1141,24 @@
 s.users(
+_.union(
 this.use
@@ -1161,24 +1161,18 @@
 .users()
-.concat(
+, 
 users));
@@ -1299,24 +1299,64 @@
 n (album) %7B%0A
+            if (!album.displayed) %7B%0A    
             
@@ -1371,32 +1371,36 @@
 splayed = true;%0A
+    
             albu
@@ -1432,32 +1432,46 @@
 elayout, this);%0A
+            %7D%0A
         %7D, this)
@@ -1492,16 +1492,24 @@
 .albums(
+_.union(
 this.alb
@@ -1513,24 +1513,18 @@
 albums()
-.concat(
+, 
 albums))
@@ -3769,17 +3769,17 @@
  1000 / 
-1
+6
 0);%0A    

Increase render speed and fix double adding users/albums