Published
Edited
Nov 4, 2019
Also listed in…
Generative Art
Insert cell
md`# Curves`
Insert cell
{
const width = 500
const height = width
let ctx = DOM.context2d(width, height);
const scale = 10
const nScale = 0.02
const cols = Math.floor(width/scale)
const rows = Math.floor(height/scale)
const field = []
for (let x = 0 ; x < cols ; x++) {
for (let y = 0 ; y < rows ; y++) {
const angle = noise(x*nScale,y*nScale,0)*2*Math.PI
const dx = Math.cos(angle);
const dy = Math.sin(angle);
const index = x + y * cols
field[index] = new Vector(dx, dy)
}
}
const particles = Array.from({length: 100}).map(() => new Particle(Math.random()*width, Math.random()*height))
function render() {
ctx.fillStyle = "rgb(255,255,255, 0.001)"
ctx.fillRect(0, 0, width, height);
// ctx.beginPath()
// field.forEach((d,idx) => {
// const x = (idx % cols) * scale
// const y = Math.floor(idx / cols) * scale
// const r = 8
// ctx.moveTo(x, y);
// ctx.lineTo(x + r*d.x, y + r*d.y);
// })
// ctx.stroke()
particles.forEach(p => {
p.follow(field, scale, cols)
p.update()
p.edges(width, height)
p.draw(ctx)
})
}
let raf
const lines = particles.map(() => [])
let currentFrame = 0;
const maxFrames = 500
function animate() {
if (currentFrame++ < maxFrames) {
render();
raf = requestAnimationFrame( animate );
lines.forEach((line, idx) => {
line.push(particles[idx].pos)
})
} else {
console.log(lines)
}
}
animate()
return ctx.canvas;
}
Insert cell
class Particle {
constructor(x=0, y=0) {
this.pos = new Vector(x, y);
this.vel = new Vector(0, 0);
this.acc = new Vector(0, 0);
this.maxspeed = 2;
this.prevPos = this.pos.clone();
}
update() {
this.vel = this.vel.add(this.acc).limit(this.maxspeed)
this.pos = this.pos.add(this.vel);
this.acc = this.acc.scale(0);
}
follow(field, scale, cols) {
let x = Math.floor(this.pos.x / scale);
let y = Math.floor(this.pos.y / scale);
let index = x + y * cols;
let v = field[index];
if (v !== undefined) {
// console.log(this.pos.x, this.pos.y, x, y, index)
this.applyForce(v.scale(0.03))
}
// console.log(v.scale(0.03))
// this.applyForce(v.scale(0.03))
}
applyForce(force) {
this.acc = this.acc.add(force);
}
draw(context) {
context.beginPath();
context.moveTo(this.prevPos.x, this.prevPos.y);
context.lineTo(this.pos.x, this.pos.y);
context.stroke();
this.updatePrev();
// context.beginPath();
// context.moveTo(this.pos.x, this.pos.y)
// context.arc(this.pos.x, this.pos.y, 2, 0, 2*Math.PI);
// context.fillStyle = "red"
// context.fill()
}
updatePrev() {
this.prevPos.x = this.pos.x;
this.prevPos.y = this.pos.y;
}
edges(width, height) {
if (this.pos.x > width) {
this.pos.x = 0;
this.updatePrev();
}
if (this.pos.x < 0) {
this.pos.x = width;
this.updatePrev();
}
if (this.pos.y > height) {
this.pos.y = 0;
this.updatePrev()
}
if (this.pos.y < 0) {
this.pos.y = height;
this.updatePrev();
}
}
}
Insert cell
Insert cell
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