### Expecto Patronum ðŸª„

or Avada Kedavra or Wingardium Leviosa. Actually the chances of getting one of those is pretty slim. You'll probably get a light trail of a random spell...

```// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(-.15);

const population = 0; //--min=0 max=2 step=1 (None, Square, Circle)
const turtles = 130; //min=50 max=300 step=10
const steps = 150; //min=50 max=500 step=10
const initLocation = 100; //min=40 max=150 step=10
const initDirection = 2500; //min=0 max=5000 step=5
const directionClamp = 2; //min=0.01 max=5 step=.01
const directionShare = 2; //min=.1 max=5 step=.1

const maxLocation = initLocation;
const maxDirection = 10/(initDirection === 0? 0.001: initDirection);

//
// 2D Vector math
//

function normalize2(a) { const length = len2(a); return scale(a,length<0.0001?1:1/length); }
function len2(a) { return Math.sqrt(lensq2(a)); }
function lensq2(a) { return dot2(a,a); }
function rot2(a) { return [Math.cos(a), -Math.sin(a), Math.sin(a), Math.cos(a)]; }
function trans2(m, a) { return [m[0]*a[0]+m[2]*a[1], m[1]*a[0]+m[3]*a[1]]; }
function scale2(a,b) { return [a[0]*b,a[1]*b]; }
function add2(a,b) { return [a[0]+b[0],a[1]+b[1]]; }
function sub2(a,b) { return [a[0]-b[0],a[1]-b[1]]; }
function dist2(a,b) { return Math.hypot(...sub2(a,b)); }
function lerp2(a,b,t) { return [a[0]*(1-t)+b[0]*t,a[1]*(1-t)+b[1]*t]; }
function dot2(a,b) { return a[0]*b[0]+a[1]*b[1] }
function mulf2(v, f) { return scale2(v,f); }
function multiply2(a2x2, a) { return [(a[0]*a2x2[0])+(a[1]*a2x2[1]),(a[0]*a2x2[2])+(a[1]*a2x2[3])]; }

function clamp(i, min, max) {
return Math.min(Math.max(i, min), max);
}

let fireflies = [];

class Firefly {
turtle = null;
position = [0, 0];
direction = [0, 0];
constructor(position, direction) {
this.turtle = new Turtle();

this.position = position;
this.direction = direction;

this.turtle.jump(this.position);
}
calibrate(fireflies) {
let closestFly = null;
let closestDistanceSquared = 90000;

let that = this;
for(let firefly of fireflies.filter(function (item) { return item !== that && item !== null; })) { //for some reason nulls exist sometimes in the collection
let d = dot2(that.position, firefly.position);
if(d < closestDistanceSquared) {
closestDistanceSquared = d;
closestFly = firefly;
}
}

let distanceVector = sub2(closestFly.position, this.position);

this.direction[0] += clamp(distanceVector[0] / directionShare, -directionClamp, directionClamp);
this.direction[1] += clamp(distanceVector[1] / directionShare, -directionClamp, directionClamp);
}
go() {
this.position[0] += this.direction[0];
this.position[1] += this.direction[1];

this.turtle.goto(this.position);
}
distanceSquared(toFirefly) {
return Math.pow(this.position[0] - toFirefly.position[0], 2) + Math.pow(this.position[1] - toFirefly.position[1], 2);
}
}

(function populate() {
for(let i = 0; i < turtles; i++) {
let x = 0;
let y = 0;

switch(population) {
case 0:
break;
case 1:
x = (Math.random() - .5) * maxLocation;
y = (Math.random() - .5) * maxLocation;
break;
case 2:
let r = this.R * Math.sqrt(Math.random()) * Math.PI / 2;
let theta = Math.random() * 2 * Math.PI;

x = Math.cos(theta) * (maxLocation / 2);
y = Math.sin(theta) * (maxLocation / 2);
break;
}

fireflies.push(new Firefly(
[x, y],
[(Math.random() - .5) * maxDirection, (Math.random() - .5) * maxDirection]
));
}
})();

// The walk function will be called until it returns false.
function walk(i) {
for(let firefly of fireflies) {
firefly.calibrate(fireflies);
firefly.go();
}
return i < steps;
}```