Published
Edited
Jun 11, 2022
6 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
birds = d3.range(numBirds).map(function (d, idx) {
return {
position: new Vec2(Math.random() * width, Math.random() * height),
velocity: randomVelocity(),
color: colorFunction.function(idx / numBirds),
cache: [] // Cache of previous velocities
};
})
Insert cell
function clearCanvas(ctx, offscreenCtx) {
// Offscreen canvas
offscreenCtx.clearRect(0, 0, width, height);
offscreenCtx.drawImage(ctx.canvas, 0, 0, width, height);

// Main canvas
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, width, height);
ctx.drawImage(offscreenCtx.canvas, 0, 0, width, height);
}
Insert cell
Insert cell
Insert cell
Insert cell
// Create random velocities between -1 and 1
randomVelocity = () =>
new Vec2(1 - Math.random() * 2, 1 - Math.random() * 2).multiply(maxSpeed)
Insert cell
Insert cell
Insert cell
Insert cell
// Heavily based of https://www.npmjs.com/package/vec2
function Vec2(x, y) {
this.x = x ?? 0;
this.y = y ?? 0;
this.count = 0;
return this;
}
Insert cell
(Vec2.prototype = {
add(v) {
this.x += v.x;
this.y += v.y;
return this;
},
subtract(v) {
this.x -= v.x;
this.y -= v.y;
return this;
},
multiply(s) {
this.x *= s;
this.y *= s;
return this;
},
scaleTo(s) {
const length = this.length();
this.x = (this.x * s) / length;
this.y = (this.y * s) / length;
return this;
},
normalize() {
const length = this.length();
this.x /= length;
this.y /= length;
return this;
},
truncate(max) {
const length = this.length();
if (length > max) {
this.x = (this.x * max) / length;
this.y = (this.y * max) / length;
}
return this;
},
length() {
return Math.sqrt(this.x ** 2 + this.y ** 2);
},
dot(v) {
return this.x * v.x + this.y * v.y;
},
clone() {
return new Vec2(this.x, this.y);
}
})
Insert cell
Insert cell
colorFunctions = [
{ name: "Blues", function: d3.interpolateBlues },
{ name: "BrBG", function: d3.interpolateBrBG },
{ name: "BuGn", function: d3.interpolateBuGn },
{ name: "BuPu", function: d3.interpolateBuPu },
{ name: "Cividis", function: d3.interpolateCividis },
{ name: "Cool", function: d3.interpolateCool },
{ name: "Cubehelix", function: d3.interpolateCubehelix },
{ name: "CubehelixDefault", function: d3.interpolateCubehelixDefault },
{ name: "CubehelixLong", function: d3.interpolateCubehelixLong },
{ name: "GnBu", function: d3.interpolateGnBu },
{ name: "Greens", function: d3.interpolateGreens },
{ name: "Greys", function: d3.interpolateGreys },
{ name: "Hcl", function: d3.interpolateHcl },
{ name: "HclLong", function: d3.interpolateHclLong },
{ name: "Hue", function: d3.interpolateHue },
{ name: "Inferno", function: d3.interpolateInferno },
{ name: "Lab", function: d3.interpolateLab },
{ name: "Magma", function: d3.interpolateMagma },
{ name: "Oranges", function: d3.interpolateOranges },
{ name: "OrRd", function: d3.interpolateOrRd },
{ name: "PiYg", function: d3.interpolatePiYG },
{ name: "Plasma", function: d3.interpolatePlasma },
{ name: "PRGn", function: d3.interpolatePRGn },
{ name: "PuBu", function: d3.interpolatePuBu },
{ name: "PuBuGn", function: d3.interpolatePuBuGn },
{ name: "PuOr", function: d3.interpolatePuOr },
{ name: "PuRd", function: d3.interpolatePuRd },
{ name: "Purples", function: d3.interpolatePurples },
{ name: "Rainbow", function: d3.interpolateRainbow },
{ name: "RdBu", function: d3.interpolateRdBu },
{ name: "RdGy", function: d3.interpolateRdGy },
{ name: "RdPu", function: d3.interpolateRdPu },
{ name: "RdYlBu", function: d3.interpolateRdYlBu },
{ name: "RdYlGn", function: d3.interpolateRdYlGn },
{ name: "Reds", function: d3.interpolateReds },
{ name: "Sinebow", function: d3.interpolateSinebow },
{ name: "Spectral", function: d3.interpolateSpectral },
{ name: "Turbo", function: d3.interpolateTurbo },
{ name: "Viridis", function: d3.interpolateViridis },
{ name: "Warm", function: d3.interpolateWarm },
{ name: "YlGn", function: d3.interpolateYlGn },
{ name: "YlGnBu", function: d3.interpolateYlGnBu },
{ name: "YlOrBr", function: d3.interpolateYlOrBr },
{ name: "YlOrRd", function: d3.interpolateYlOrRd }
]
Insert cell
d3 = require("d3@7.4.4")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more