function flock(canvas, totalBoids, yearValues, theGroupImages, theGroupColours, velocity) {
	if (typeof(velocity) == 'undefined') {
		velocity = 1;
	}

	var index = 0;
	document.title = 'Data for ' + years[index];
	var groupArray = JSON.parse(JSON.stringify(yearValues[index]));
	var groupImages = JSON.parse(JSON.stringify(theGroupImages));
	var groupColours = JSON.parse(JSON.stringify(theGroupColours));
	var groups = [];

	var updateIt = false;
	$(document).keyup(function() {
		index++;

		if (index == yearValues.length) index = 0;
		document.title = 'Data for ' + years[index];

		var tempGroupArray = JSON.parse(JSON.stringify(yearValues[index]));
		var tempGroupImages = JSON.parse(JSON.stringify(theGroupImages));
		var tempGroupColours = JSON.parse(JSON.stringify(theGroupColours));

		cullGroups(tempGroupArray, tempGroupImages, tempGroupColours);

		var tempScale = getScale(tempGroupArray);

		var existingGroupCount=0;
		for (var i in groupArray) {
			var tempIndex = exists(groupImages[i], tempGroupImages);
			if (tempIndex != -1) { // party still exists
				var tempValue = Math.floor((tempScale * tempGroupArray[tempIndex]/min));
				console.log('We know ' + tempGroupImages[tempIndex] + ' new value ' + tempValue + ' old value ' + groupArray[i]);

				if (tempValue === groupArray[i]) {
					console.log('No change.');
					continue;
				}

				if (tempValue < groupArray[i]) {
					var remove = Math.abs(tempValue - groupArray[i]);
					console.log('Remove from ' + tempGroupImages[tempIndex] + ': ' + remove);

					for (var boid = 0; boid < remove; boid++) {
						groups[i][boid].dead = true;
						console.log('dead');
					}
				}
				else {
					var add = Math.abs(tempValue - groupArray[i]);

					console.log('Add ' + add + " to " + tempGroupImages[tempIndex]);
					for (var added = 0; added < add; added++) {
						addNewBoidTo(groups[i]);
					}
				}

				groupArray[i] = tempValue;
			} else { // party is gone
				console.log('Bye ' + groupImages[i]);
				for (var j in groups[i]) {
					groups[i][j].dead = true;
				}

				groupArray[i] = 0;
			}
		}

		function addNewBoidTo(group) {
			var x = -Math.random() * window.innerWidth/5;
    		var y = -Math.random() * window.innerHeight/5;
    		var dir = {'x': Math.random(), 'y': Math.random()};

			group.push({'x': x, 'y': y, velocity: dir});
		}

		for (var i in tempGroupArray) {
			var tempIndex = exists(tempGroupImages[i], groupImages);
			if (tempIndex == -1) { 
				var tempValue = Math.floor((tempScale * tempGroupArray[i]/min));
				console.log('New party: ' + tempGroupImages[i] + ' value: ' + tempValue) ;
				var newGroup = [];

				groupArray.push(tempValue);
				groupImages.push(tempGroupImages[i]);
				groupColours.push(tempGroupColours[i]);

				for (var j = 0; j < tempValue; j++) {
		    		addNewBoidTo(newGroup);
		    	}

		    	groups.push(newGroup);
			}
		}
		// groupArray = JSON.parse(JSON.stringify(yearValues[index]));
		// groupImages = JSON.parse(JSON.stringify(theGroupImages));
		// groupColours = JSON.parse(JSON.stringify(theGroupColours));
		// updateIt = true;
	});

	flockGroup();

	function getScale(groupArray) {
			var tempTotal = 0;
			var scale = 0;
			for (var i in groupArray) {
				tempTotal += Math.ceil(groupArray[i] / min);
			}
			scale = totalBoids / tempTotal;
			if (scale < 1) scale = 1;
			return scale;
		}

	var min;
	var totalCount;

	function cullGroups(groupArray, groupImages, groupColours) {

	    totalCount = 0;
	    min = 0;
	    var max;
	    var cutOff = 0, cutOffAmount=0;
	    while (cutOff < 0.05) {
	    	min = 0;

			for (var i in groupArray) {
				if (!min || (groupArray[i] && groupArray[i] < min && (cutOff && groupArray[i] > cutOffAmount))) {
					min = groupArray[i];
				}

				if (!max || groupArray[i] > max) {
					max = groupArray[i];
				}
			}

			cutOff = min / max;
			cutOffAmount = (cutOff * max)+1;

			if (cutOff < 0.05) {
				for (var i = 0; i < groupArray.length;) {
					if (groupArray[i] < cutOffAmount) {
						groupArray.splice(i, 1);
						groupImages.splice(i, 1);
						groupColours.splice(i, 1);
					} else i++;
				}
			}
		}
	}

	function flockGroup() {
		canvas.width  = window.innerWidth;
	 	canvas.height = window.innerHeight;

		var TAU = Math.PI * 2;
		var THRESHOLD = 8000;
		var ZERO = {'x':0, 'y':0};
		var VELOCITY_CORRECTION = 0.52;
		var MAX_TTL = 10;
		var BORDER = 100;
		var IMAGE_SIZE = 30;

		var ctx = canvas.getContext("2d");
		var width = canvas.width;
		var height = canvas.height;

	    
	    var trails = [];

	    cullGroups(groupArray, groupImages, groupColours);
	    
		var images=[];
		for (var i in groupImages) {
			images[i] = new Image();
			images[i].src = groupImages[i];
		}

		var scale = getScale(groupArray);

	    for (var i in groupArray) {
	    	var group = [];

	    	groupArray[i] = Math.floor(scale * (groupArray[i]/min));
	    	totalCount += groupArray[i];
	    	for (var j = 0; j < groupArray[i]; j++) {
	    		var x = Math.random() * width;
	    		var y = Math.random() * height;
	    		var dir = {'x': Math.random(), 'y': Math.random()};

	    		group.push({'x': x, 'y': y, velocity: dir});
	    	}

	    	groups.push(group);
	    }

		drawGroups();

		update();

		function updateGroup(group, index) {
			var remove = [];
			for (var i in group) {
				if (!group[i].dead) {
					updateVelocity(group[i], group);
					trails.push({'TTL':MAX_TTL, 'x':group[i].x, 'y':group[i].y, 'colour':groupColours[index]});

					group[i].x += group[i].velocity.x;
					group[i].y += group[i].velocity.y;
				} else {
					if (group[i].velocity.x < 1) {
						group[i].velocity.x = 1;
					}

					if (group[i].velocity.y < 1) {
						group[i].velocity.y = 1;
					}

					group[i].x += 3*group[i].velocity.x;
					group[i].y += 3*group[i].velocity.y;

					if (group[i].x > canvas.innerWidth || group[i].x < 0 || group[i].y < 0 || group[i].y > innerHeight ) {
						remove.push(i);
					}
				}

				if (!group[i].dead) {
					if (group[i].x < BORDER) {
						group[i].velocity.x += VELOCITY_CORRECTION;
						group[i].velocity.x = Math.min(group[i].velocity.x, 5*VELOCITY_CORRECTION);
					}

					if (group[i].x > canvas.width - BORDER) {
						group[i].velocity.x += -VELOCITY_CORRECTION;
						group[i].velocity.x = Math.max(group[i].velocity.x, 5*-VELOCITY_CORRECTION);
					}

					if (group[i].y < BORDER) {
						group[i].velocity.y += VELOCITY_CORRECTION;
						group[i].velocity.y = Math.min(group[i].velocity.y, 5*VELOCITY_CORRECTION);
					}
					
					if (group[i].y > canvas.height - BORDER) {
						group[i].velocity.y += -VELOCITY_CORRECTION;
						group[i].velocity.y = Math.max(group[i].velocity.y, 5*-VELOCITY_CORRECTION);
					}
				}
			}

			for (var i in remove) {
				console.log("Offscreen, removing.");
				group.splice(i, 1);

			}
		}

		function updateVelocity(boid, group) {
			var neighbours = getNeighbours(boid, group);

			var centerVelocity = multiply(getCenter(boid, neighbours), 1);

			var distanceVelocity = getDistanceVelocity(boid, neighbours);

			var averageVelocity = getAverageVelocity(boid, neighbours);

			// drawCircle(middle.x, middle.y, 15);

			var resultVelocity = add(add(add(boid.velocity, centerVelocity), distanceVelocity), averageVelocity);

			if (getDistance(ZERO, resultVelocity) < 50) {
				boid.velocity = resultVelocity;
			}
		}

		function getAverageVelocity(boid, neighbours) {
			var velocity = ZERO;

			for (var i in neighbours) {
				velocity = add(velocity, neighbours[i].velocity);;
			}

			velocity = divide(velocity, neighbours.length);
			return divide(subtract(velocity, boid.velocity), 8)
		}

		function getDistanceVelocity(boid, neighbours) {
			var center = {'x':0, 'y':0};

			for (var i in neighbours) {
				if (getDistance(boid, neighbours[i]) < 50) {
					center = subtract(center, subtract(boid, neighbours[i]));
				}
			}

			return center;
		}

		function subtract(vectorOne, vectorTwo) {
			return {'x':(vectorOne.x - vectorTwo.x), 'y':(vectorOne.y - vectorTwo.y)};
		}

		function add(vectorOne, vectorTwo) {
			return {'x':(vectorOne.x + vectorTwo.x), 'y':(vectorOne.y + vectorTwo.y)};
		}

		function divide(vector, scalar) {
			return {'x':(vector.x / scalar), 'y':(vector.y / scalar)};	
		}

		function multiply(vector, scalar) {
			return {'x':(vector.x * scalar), 'y':(vector.y * scalar)};	
		}

		function getCenter(boid, neighbours) {
			var allX=0, allY=0;
			for (var i in neighbours) {
				allX += neighbours[i].x;
				allY += neighbours[i].y;
			}

			var middle = {};
			middle.x = allX / neighbours.length;
			middle.y = allY / neighbours.length;

			return divide(subtract(middle, boid), 500);
		}

		function getDistance(one, two) {
			var diffX = one.x - two.x;
			var diffY = one.y - two.y;

			return (diffX*diffX + diffY*diffY);
		}

		function getNeighbours(boid, group) {
			var neighbours = [];
			var total = 0;

			for (var i in group) {
				if (boid != group[i]) {
					var dist = getDistance(boid, group[i]) ;
					total += dist;
					if (dist < THRESHOLD) {
						neighbours.push(group[i]);
					}
				}
			}

			return neighbours;
		}

		function update() {
			canvas.width = canvas.width;

			forAllGroups(updateGroup);

			drawGroups();

			if (!updateIt) {
				requestAnimationFrame(update);
			} else {
			  updateIt = false;
			  flockGroup();
			}
		}

		function forAllGroups(action) {
			for (var i in groups) {
	    		action(groups[i], i);
	    	}
		}

	    function drawGroups() {
	    	

	    	for (var i = 0; i < trails.length; ) {
	    		ctx.globalAlpha = trails[i].TTL / 10;
	    		drawCircle(trails[i].x, trails[i].y, 5, trails[i].colour);
	    		trails[i].TTL--;
	    		if (!trails[i].TTL) {
	    			trails.splice(i, 1);
	    		} else i++;
	    	}
			ctx.globalAlpha = 1;


	    	forAllGroups(drawGroup)
	    }

	    function drawGroup(group, index) {
	    	if (!images[index])
	    		return;

	    	for (var i in group) {
	    		if (images[index].src.indexOf('w.png') == -1)
	    			ctx.drawImage(images[index], group[i].x-IMAGE_SIZE/2, group[i].y-IMAGE_SIZE/2, IMAGE_SIZE, IMAGE_SIZE);
	    		else ctx.drawImage(images[index], group[i].x, group[i].y, 1, 1);
	    	}
	    }

		function drawCircle(x, y, radius, colour) {
			if (ctx.fillStyle != colour) {
				ctx.fillStyle = colour;
			}
			ctx.beginPath();
			ctx.arc(x,y,radius,0,2*Math.PI);
			ctx.fill();
		}
	}
}