setup: _setup = (sketch, globals) => {},
draw: _draw = (sketch, time, globals) => sketch.background(sketch.sin(time * sketch.TAU) * 255),
return new p5(sketch => {
attribute vec3 aPosition;
gl_Position = vec4(aPosition, 1.0);
uniform sampler2D ${array(samplesPerFrame).map(i => `tex${i}`).join(', ')};
vec2 pos = gl_FragCoord.xy / resolution.xy;
// vec3 c = texture2D(tex0, pos).rgb;
for(int i = 0; i < 3; i ++) {
vec2 p = (pos - 0.5) * (1. + 0.008 * float(i) * float(${chromaticAberration})) + 0.5;
${array(samplesPerFrame).map((d, i) => `c[i] += texture2D(tex${i}, p)[i];`).join('\n ')}
c /= ${samplesPerFrame}.0;
gl_FragColor = vec4(c, 1.0);
sketch.createCanvas(w, h, record ? sketch.WEBGL : webgl ? sketch.WEBGL : undefined)
if(!play) sketch.noLoop()
sketch.canvas.addEventListener('click', () => {
play ? sketch.noLoop() : sketch.loop()
recorder = video ? new WebMWriter({ quality: 0.99, frameRate: fps }) : new GIF()
for (let frame = 0; frame < numFrames; frame++) {
for (let sample = 0; sample < samplesPerFrame; sample++) {
time.push((frame + sample * shutterAngle / samplesPerFrame) / numFrames)
beesAndBombsShader = sketch.createShader(vs, fs)
sketch.shader(beesAndBombsShader)
beesAndBombsShader.setUniform('resolution', [sketch.width, sketch.height])
for (let i = 0; i < samplesPerFrame; i++) {
textures[i] = sketch.createGraphics(sketch.width, sketch.height, webgl ? sketch.WEBGL : undefined)
textures[i].pixelDensity(1)
beesAndBombsShader.setUniform(`tex${i}`, textures[i])
_setup(textures[i], globals)
_draw(sketch, ((sketch.frameCount - 1) / numFrames) % 1, globals)
for (let i = 0; i < samplesPerFrame; i++) {
_draw(textures[i], time[i + ((sketch.frameCount - 1) % numFrames) * samplesPerFrame], globals)
sketch.quad(-1, -1, -1, 1, 1, 1, 1, -1)
if (video) recorder.addFrame(sketch.canvas)
else recorder.addFrame(sketch.canvas, { copy: true, delay: 1000 / fps, quality: 1 })
if (sketch.frameCount === numFrames * loops || sketch.frameCount === preview) {
recorder.complete().then(_blob => blob = _blob)
recorder.on('finished', _blob => blob = _blob)
array = n => new Array(n).fill(0).map((d, i) => i)
let tmpArray = [ ... arr ]
for (let i = tmpArray.length - 1; i; i --) {
let randomIndex = randInt(i + 1);
[tmpArray[i], tmpArray[randomIndex]] = [tmpArray[randomIndex], tmpArray[i]]
ease = (p, g) => !g ? 3 * p * p - 2 * p * p * p :
p < 0.5 ? 0.5 * Math.pow(2 * p, g) :
1 - 0.5 * Math.pow(2 * (1 - p), g)
clamp = (a, min, max) => a < min ? min : a > max ? max : a
linearstep = (t, begin, end) => clamp((t - begin) / (end - begin), 0, 1)
if(!a && a !== 0) return Math.random()
if(!b && b !== 0) return Math.random() * a
if(a > b) [a, b] = [b, a]
return a + Math.random() * (b - a)
randInt = (a, b) => ~~random(a, b)
coin = () => random() < .5 ? -1 : 1
PVector = (a,b,c,d) => new p5.Vector(a,b,c,d)
p = [ "#65524D", "#817E9F", "#7FC29B","#B5EF8A","#D7F171"]
palette = ["#65524D","#373f51", "#d8dbe2","#1b1b1e", "#d8dbe2","#086788", "#F0C808"]
this.b = PVector(7 * step * coin(), 7 * step * coin()).add(this.a)
if(this.b.x < step || this.b.x > (n - 1) * step) {
if(this.b.y < step || this.b.y > (n - 1) * step) {this.b.y -= this.a.y;
this.dir = s.random([PVector(1, 0), PVector(0, 1)]).mult(randInt(5, 30))
this.offset = s.random(1)
this.color = palette[randInt(1, palette.length)]
if((t + this.offset) % 1 > .65) return;
const A = p5.Vector.lerp(this.a, this.b, ease(linearstep((t + this.offset) % 1, 0, .3), 3.5))
const B = p5.Vector.lerp(this.a, this.b, ease(linearstep((t + this.offset) % 1, .4, .7), 3.5))
A.x - this.dir.x, A.y - this.dir.y,
A.x + this.dir.x, A.y + this.dir.y,
B.x + this.dir.x, B.y + this.dir.y,
B.x - this.dir.x, B.y - this.dir.y
(w - margin * 2) - (A.x - this.dir.x), A.y - this.dir.y,
(w - margin * 2) - (A.x + this.dir.x), A.y + this.dir.y,
(w - margin * 2) - (B.x + this.dir.x), B.y + this.dir.y,
(w - margin * 2) - (B.x - this.dir.x), B.y - this.dir.y
g.slashes = g.slashes.map(i => new Slash(randInt(1, n - 1) * step, randInt(1, n - 1) * step))
s.translate(margin, margin)
s.drawingContext.globalAlpha = 0.6
for(let x = 0; x <= s.width - (margin * 2); x += step) {
for(let y = 0; y <= s.height - (margin * 2); y += step) {
if(x % 125 === 0 && y % 125 === 0) {
s.rotate(linearstep(0.2, 0.5, t) * s.PI)
g.slashes.forEach(slash => slash.display(s, t))