bnb({
w, h,
numFrames: 120,
fps: 20,
record: true,
preload: (s, g) => {
const pg = s.createGraphics(g.w, g.h)
pg.background(30)
pg.noFill()
pg.stroke(255)
pg.strokeWeight(20)
const diam = s.min(g.w, g.h)/10 * 9
pg.beginShape()
for(let i = 0; i < 3; i ++) {
const angle = s.TAU / 3. * i - s.PI/2
const x = g.w / 2 + s.cos(angle) * diam / 2
const y = g.h / 5 * 3 + s.sin(angle) * diam / 2
pg.vertex(x, y)
}
pg.endShape(s.CLOSE)
g.pg = pg
},
setup: (s, g) => {
g.prng = new PRNG(prngSeed)
g.noise = new SimplexNoise(simplexSeed)
const noiseScale = 450
const noiseStrength = 5
class Path {
constructor(x, y) {
this.points = []
for(let j = 0; j < g.numFrames; j++) {
const v = PVector(x, y)
const pts = [v.copy()]
for(let i = 0; i < g.numFrames; i++) {
const angle = simplex.noise4D(v.x/noiseScale, v.y/noiseScale, s.cos(j/g.numFrames * TAU) * .6, s.sin(j/g.numFrames * TAU) * .6) * TAU
v.add(PVector.fromAngle(angle).mult(noiseStrength))
pts.push(v.copy())
}
this.points.push(pts)
}
}
display(s, t) {
s.noFill()
this.points.forEach((pts, i) => {
if(i === g.frame) {
s.stroke(255)
s.beginShape()
pts.forEach(p => s.vertex(p.x, p.y))
s.endShape()
}
})
}
}
class Particle {
constructor(x, y) {
this.c = palette[randInt(1, palette.length)]
this.path = g.paths[randInt(g.paths.length)]
this.offset = randInt(g.numFrames)
}
display(s, t) {
const tt = (g.frame + this.offset) % g.numFrames
s.drawingContext.globalAlpha = tt / g.numFrames
s.noStroke()
s.fill(this.c)
const v = this.path.points[this.offset][tt]
s.circle(v.x, v.y, -s.sin(tt / g.numFrames * s.PI) * 10)
s.drawingContext.globalAlpha = 1
}
}
g.paths = []
while(g.paths.length < 1000) {
const x = g.prng.rand(0, g.w)
const y = g.prng.rand(0, g.h)
if(s.brightness(g.pg.get(x, y)) > 50) g.paths.push(new Path(x, y))
}
g.particles = array(20000).map(i => new Particle())
},
draw: (s, t, g) => {
s.background(palette[0])
g.particles.forEach(p => p.display(s, t))
}
})