canvas {display:block; margin: 20px auto; border: 2px solid #4892c6; max-width: 100%; box-sizing: border-box; }
// Everything above -3 is boring
// Rate of depth color change
var _live = document.createElement('canvas').getContext('2d');
_live.canvas.width = _options.width * _options.scale;
_live.canvas.height = _options.height * _options.scale;
document.body.appendChild(_live.canvas);
var _offscreen = _live.canvas.cloneNode().getContext('2d');
_offscreen.lineWidth = _options.scale * .2;
_offscreen.lineCap = 'round';
_offscreen.strokeStyle = '#000';
var root = addPoint([~~(_options.width / 2), ~~(_options.height / 2)]);
renderPoint(_offscreen, root);
var i = _options.renderInterval;
renderPoint(_offscreen, point);
if(--i <= 0 && _options.delay >= 0) {
setTimeout(play, _options.delay);
var cell = removeIndex(_stack, _options.cellIndex);
var edge = removeIndex(edges, _options.edgeIndex);
added = addPoint(edge, cell);
// Registers and initializes the given point and adds it to the stack.
function addPoint(point, parent) {
var o = getOffset(point);
p.parent = parent || null;
p.depth = parent ? parent.depth + 1 : 0;
// Returns a string ID for a point.
function getOffset(point) {
// Checks if a point is inside the extent.
function isInBounds(point) {
return point[0] >= 0 && point[0] < _options.width
&& point[1] >= 0 && point[1] < _options.height;
// Checks if a point's offset is not yet blocked.
function isAvailable(point) {
return !_points.has(getOffset(point));
// Normalizes an index and extracts the element at the resulting index.
function removeIndex(arr, index) {
? Math.max(0, arr.length + index)
: Math.min(arr.length - 1, index);
return arr.splice(i, 1)[0];
// Produces edge coordinates for a point.
function renderPoint(ctx, point) {
var s = _options.scale, sh = s / 2;
var level = ~~(70 + 185 * Math.abs(Math.cos(point.depth * _options.depthScale)));
ctx.fillStyle = 'rgb(' + level + ',' + level + ',' + level + ')';
ctx.fillRect(x, y, s, s);
if(_options.drawLines && point.parent) {
ctx.moveTo(point.parent[0] * s + sh, point.parent[1] * s + sh);
ctx.lineTo(x + sh, y + sh);
_live.clearRect(0, 0, _live.canvas.width, _live.canvas.height);
_live.drawImage(_offscreen.canvas, 0, 0);