Another possibility is to give the nodes something like mass and take it into account in the collision function. You can then turn on gravity and let them fight for position.
This example gets there after a small flurry.
Here is a modified collision function ...
function Collide(nodes, padding) { // Resolve collisions between nodes. var maxRadius = d3.max(nodes, function(d) {return d.radius}); return function collide(alpha) { var quadtree = d3.geom.quadtree(nodes); return function(d) { var r = d.radius + maxRadius + padding, nx1 = dx - r, nx2 = dx + r, ny1 = dy - r, ny2 = dy + r; quadtree.visit(function(quad, x1, y1, x2, y2) { var possible = !(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1); if (quad.point && (quad.point !== d) && possible) { var x = dx - quad.point.x, y = dy - quad.point.y, l = Math.sqrt(x * x + y * y), r = d.radius + quad.point.radius + padding, m = Math.pow(quad.point.radius, 3), mq = Math.pow(d.radius, 3), mT = m + mq; if (l < r) { //move the nodes away from each other along the radial (normal) vector //taking relative mass into consideration, the sign is already established //in calculating x and y and the nodes are modelled as spheres for calculating mass l = (r - l) / l * alpha; dx += (x *= l) * m/mT; dy += (y *= l) * m/mT; quad.point.x -= x * mq/mT; quad.point.y -= y * mq/mT; } } return !possible; }); }; } }
Forced schedule with custom sorting nodes - Position swap

Features
- Accelerated annealing
Annealing is released every time, but until alpha drops below 0.05, only every nth tick is updated visas (n is currently 4). This significantly reduces the time to reach equilibrium (about 2 times). - Power dynamics
Power dynamics is a two-phase alpha function. The initial phase has zero charge, low gravity and low attenuation. This is done for maximum mixing and sorting. The second phase has higher gravity and a large negative charge and significantly higher damping, which is designed to clean and stabilize the representation of nodes. - Collisions of nodes
Based on this example, but extended to sort the radial position of nodes based on size, with larger nodes closer to the center. Each collision is used as an opportunity to correct relative positions. If they are out of position, then the radial ordinates of the colliding nodes (in polar coordinates) are interchanged. Therefore, sorting efficiency depends on good mixing in collisions. To maximize mixing, all nodes are created at the same point in the center of the graph. When nodes are swapped, their speeds are maintained. This is done by changing the previous points ( p.px and p.py ). The mass is calculated under the condition that the nodes are spheres using r 3 and the selections are calculated by relative "mass".
Eject
function Collide(nodes, padding) { // Resolve collisions between nodes. var maxRadius = d3.max(nodes, function(d) { return dqradius }); return function collide(alpha) { var quadtree = d3.geom.quadtree(nodes); return function(d) { var r = d.radius + maxRadius + padding, nx1 = dx - r, nx2 = dx + r, ny1 = dy - r, ny2 = dy + r; quadtree.visit(function v(quad, x1, y1, x2, y2) { var possible = !(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1); if(quad.point && (quad.point !== d) && possible) { var x = dx - quad.point.x, y = dy - quad.point.y, l = Math.sqrt(x * x + y * y), r = d.radius + quad.point.radius + padding; if(l < r) { for(; Math.abs(l) == 0;) { x = Math.round(Math.random() * r); y = Math.round(Math.random() * r); l = Math.sqrt(x * x + y * y); } ; //move the nodes away from each other along the radial (normal) vector //taking relative size into consideration, the sign is already established //in calculating x and y l = (r - l) / l * alpha; // if the nodes are in the wrong radial order for there size, swap radius ordinate var rel = d.radius / quad.point.radius, bigger = (rel > 1), rad = dr / quad.point.r, farther = rad > 1; if(bigger && farther || !bigger && !farther) { var d_r = dr; dr = quad.point.r; quad.point.r = d_r; d_r = d.pr; d.pr = quad.point.pr; quad.point.pr = d_r; } // move nodes apart but preserve their velocity dx += (x *= l); dy += (y *= l); d.px += x; d.py += y; quad.point.x -= x; quad.point.y -= y; quad.point.px -= x; quad.point.py -= y; } } return !possible; }); }; } }
It's a little faster, but also more organic looking ...

Additional functions
Conflict Sort Events
When nodes are replaced, the speed of the larger node is maintained, and the smaller node is accelerated. In this way, sorting efficiency is improved as smaller nodes are removed from the collision point. The mass is calculated under the condition that the nodes are spheres using r 3 and the selections are calculated by relative "mass".
function Collide(nodes, padding) { // Resolve collisions between nodes. var maxRadius = d3.max(nodes, function(d) { return d.radius }); return function collide(alpha) { var quadtree = d3.geom.quadtree(nodes), hit = false; return function c(d) { var r = d.radius + maxRadius + padding, nx1 = dx - r, nx2 = dx + r, ny1 = dy - r, ny2 = dy + r; quadtree.visit(function v(quad, x1, y1, x2, y2) { var possible = !(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1); if(quad.point && (quad.point !== d) && possible) { var x = dx - quad.point.x, y = dy - quad.point.y, l = (Math.sqrt(x * x + y * y)), r = (d.radius + quad.point.radius + padding), mq = Math.pow(quad.point.radius, 3), m = Math.pow(d.radius, 3); if(hit = (l < r)) { for(; Math.abs(l) == 0;) { x = Math.round(Math.random() * r); y = Math.round(Math.random() * r); l = Math.sqrt(x * x + y * y); } //move the nodes away from each other along the radial (normal) vector //taking relative size into consideration, the sign is already established //in calculating x and y l = (r - l) / l * (1 + alpha); // if the nodes are in the wrong radial order for there size, swap radius ordinate var rel = m / mq, bigger = rel > 1, rad = dr / quad.point.r, farther = rad > 1; if(bigger && farther || !bigger && !farther) { var d_r = dr; dr = quad.point.r; quad.point.r = d_r; d_r = d.pr; d.pr = quad.point.pr; quad.point.pr = d_r; } // move nodes apart but preserve the velocity of the biggest one // and accelerate the smaller one dx += (x *= l); dy += (y *= l); d.px += x * bigger || -alpha; d.py += y * bigger || -alpha; quad.point.x -= x; quad.point.y -= y; quad.point.px -= x * !bigger || -alpha; quad.point.py -= y * !bigger || -alpha; } } return !possible; }); }; } }