<canvas id=c></canvas>
body {
	overflow-x: hidden;
}
canvas {
	position: absolute;
	top: 0;
	left: 0;
}
var	w = c.width = window.innerWidth - 10,
		h = c.height = 0,
		ctx = c.getContext( '2d' ),
		
		opts = {
			maxSteps: 25,
			allowNeg: false, // includes 0
			allowFlo: false,
			
			stepHeight: 40
		},
		
		nums = [1],
		coords = [ {x: 20 + w/2, y: opts.stepHeight / 2 } ],
		step = 0;

h = c.height = opts.maxSteps * opts.stepHeight;

function isValid( n ){
	
	return ( n === Math.floor( n ) || opts.allowFlo ) &&
		( n > 0 || opts.allowNeg ) &&
		n !== 1; // avoid 4 → 1 loop
}
function anim(){
	
	if( step <= opts.maxSteps )
		window.requestAnimationFrame( anim );
	
	ctx.lineWidth = 1;
	ctx.strokeStyle = '#ccc';
	ctx.beginPath();
	ctx.moveTo( 40.5, step * opts.stepHeight );
	ctx.lineTo( 40.5, ( step + 1 ) * opts.stepHeight );
	ctx.stroke();
	
	
	ctx.font = '20px monospace';
	var indexLen = ctx.measureText( step ).width;
	ctx.fillStyle = '#222';
	ctx.fillText( step, 20 - indexLen / 2, ( step + .5 ) * opts.stepHeight );
	
	var curNums = [],
			curRelations = [];
	
	for( var i = 0; i < nums.length; ++i ){
		
		var doubl = 2 * nums[ i ],
				third = ( nums[ i ] - 1 ) / 3;
		
		if( isValid( doubl ) ){
			curNums.push( doubl );
			curRelations.push( { formula: 2, parentInd: i } ); // 2 = 2a, 1 = (a-1)/3
		}
		if( isValid( third ) && third % 2 === 1 ){
			curNums.push( third );
			curRelations.push( { formula: 1, parentInd: i } );
		}
	}
	
	var curCoords = [];
	for( var i = 0; i < curNums.length; ++i ){
		var x = 40 + ( ( i + 1 ) / ( curNums.length + 1 ) ) * ( w - 40 ), // +1s are for space-around effect
				y = ( step + 1.5 ) * opts.stepHeight;
		curCoords.push( { x: x, y: y } );
	}
	
	var size = Math.max( Math.min( 20, ( w - 40 ) / 4 / curNums.length ), 5 );
	
	ctx.lineWidth = size + 5;
	ctx.lineCap = 'round';
	
	// draw relations first
	for( var i = 0; i < curRelations.length; ++i ){
		
		var rel = curRelations[ i ];
		ctx.strokeStyle = rel.formula === 1 ? '#fa8' : '#a8f';
		ctx.beginPath();
		ctx.moveTo( curCoords[ i ].x, curCoords[ i ].y );
		ctx.lineTo( coords[ rel.parentInd ].x, coords[ rel.parentInd ].y );
		ctx.stroke();
	}
	
	// then draw nums
	ctx.font = size / 2 + 'px monospace';
	for( var i = 0; i < nums.length; ++i ){
		ctx.fillStyle = '#eee';
		ctx.beginPath();
		ctx.arc( coords[ i ].x, coords[ i ].y, size / 2, 0, Math.PI * 2 );
		ctx.fill();
		
		ctx.fillStyle = '#222';
		
		var len = ctx.measureText( nums[ i ] ).width;
		ctx.fillText( nums[ i ], coords[ i ].x - len / 2, coords[ i ].y + size / 8 );
	}
	
	debugger;
	
	coords = curCoords;
	nums = curNums;
	
	++step;
}
anim();
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.