Bomb.pde
class Bomb {
int lifeTime;
int time;
PVector burstPos;
color fireColor;
float velocity;
Particle[] particles;
Bomb(int nparticles, int lifeTime) {
this(nparticles, lifeTime, new PVector(0.0, 0.0));
}
Bomb(int nparticles, int lifeTime, PVector burstPos) {
this(nparticles, lifeTime, burstPos, color(255, 0, 0, 128));
}
Bomb(int nparticles, int lifeTime, PVector burstPos, color fireColor) {
this(nparticles, lifeTime, burstPos, fireColor, 9.0);
}
Bomb(int nparticles, int lifeTime, PVector burstPos, color fireColor, float velocity) {
this.lifeTime = lifeTime;
this.time = 0;
this.burstPos = burstPos;
this.fireColor = fireColor;
float randomRot = random(TWO_PI);
particles = new Particle[nparticles];
for (int k = 0; k < particles.length; k++) {
float randomRadius = 0.25;
float t = (float)(k + 0.5) / particles.length * TWO_PI + randomRot;
PVector vel = new PVector(cos(t), sin(t));
PVector randomVel = new PVector(random(0.0, 1.0) - 0.5, random(0.0, 1.0) - 0.5);
PVector acl = new PVector(0.0, 0.25);
randomVel.mult(randomRadius);
vel.mult(velocity);
vel.add(randomVel);
particles[k] = new Particle(10, burstPos.copy(), vel, acl);
}
}
void update() {
if (time >= lifeTime) {
return;
}
for (Particle particle : particles) {
particle.update();
}
time = time + 1;
}
void draw() {
if (time >= lifeTime) {
return;
}
float a = alpha(fireColor);
noFill();
stroke(fireColor, a * (1.0 - smoothstep(lifeTime*0.75, lifeTime, time)));
strokeWeight(2.5);
for (Particle particle : particles) {
particle.draw();
}
}
boolean isAlive() {
return time < lifeTime;
}
}
Firework.pde
class Firework {
int time;
PVector burstPos;
PVector startPos;
PVector pos;
boolean bursted;
color[] palette;
Bomb[] bombs;
Band[] bands;
int bandpt;
Firework(PVector burstPos) {
this(burstPos, new color[] {#FF0000, #00FF00, #0000FF});
}
Firework(PVector burstPos, color[] palette) {
this.time = 0;
this.burstPos = burstPos;
this.startPos = new PVector(burstPos.x, height * 1.15);
this.pos = startPos.copy();
this.bursted = false;
this.palette = palette;
bombs = new Bomb[palette.length];
float vel = random(9.0, 12.0);
for (int k = 0; k < bombs.length; k++) {
bombs[k] = new Bomb(32, 60, burstPos, palette[k], vel * (1.25 - (float)k / bombs.length));
}
bands = new Band[20];
bandpt = 0;
}
void update() {
if (bursted) {
for (Bomb bomb : bombs) {
bomb.update();
}
} else {
pos = lerpVector(startPos, burstPos, 1.0 - pow(time / 60.0 - 1.0, 2));
addBand(pos.copy());
}
for (Band band : bands) {
if (band != null) {
band.update();
}
}
if (time >= 60) {
bursted = true;
}
time = time + 1;
}
void draw() {
if (bursted) {
for (Bomb bomb : bombs) {
bomb.draw();
}
}
for (Band band : bands) {
if (band != null) {
band.draw();
}
}
}
void addBand(PVector p) {
PVector r = new PVector(
width / 120.0 * randomGaussian(),
width / 120.0 * randomGaussian()
);
p.add(r);
bands[bandpt] = new Band(p);
bandpt = (bandpt + 1) % bands.length;
}
}
class Band {
int time;
int lifeTime;
PVector pos;
float radius;
Band(PVector pos) {
this.pos = pos;
this.radius = width / 20.0;
lifeTime = 20;
}
void update() {
if (time >= lifeTime) {
return;
}
time = time + 1;
}
void draw() {
if (time >= lifeTime) {
return;
}
noStroke();
fill(255, 240);
circle(pos.x, pos.y, lerp(radius, 0, 1.0 - pow((float)time / lifeTime - 1.0, 2)));
}
}
Particle.pde
class Particle {
int time;
PVector[] buffer;
int bufpt;
int len;
PVector pos;
PVector vel;
PVector acl;
Particle(PVector pos) {
this(60, pos);
}
Particle(PVector pos, PVector vel, PVector acl) {
this(60, pos, vel, acl);
}
Particle(int len, PVector pos) {
this(len, pos, new PVector(0.0, 0.0), new PVector(0.0, 0.0));
}
Particle(int len, PVector pos, PVector vel, PVector acl) {
this.len = len;
this.pos = pos;
this.vel = vel;
this.acl = acl;
this.time = 0;
buffer = new PVector[len];
for (int k = 0; k < buffer.length; k++) {
buffer[k] = null;
}
bufpt = 0;
}
void update() {
this.pos.add(this.vel);
if (time > 10) {
this.vel.add(this.acl);
}
buffer[bufpt] = this.pos.copy();
bufpt = (bufpt + 1) % len;
time = time + 1;
}
void draw() {
beginShape();
for (int k = 0; k < len; k++) {
PVector pos = buffer[(bufpt + k) % len];
if (pos == null) {
continue;
}
vertex(pos.x, pos.y);
}
endShape();
}
}
Utils.pde
PVector lerpVector(PVector start, PVector end, float t) {
PVector p = new PVector(
lerp(start.x, end.x, t),
lerp(start.y, end.y, t),
lerp(start.z, end.z, t)
);
return p;
}
color randomChoice() {
color[] cls = new color[] {
#D9333F, #C3D825, #2CA9E1, #EE7800, #BBBCDE, #DDDCD6, #68BE8D, #A59ACA, #FFDB4F, #00A381, #B44C97
};
return cls[(int)random(cls.length)];
}
color[][] palettes = new color[][] {
{#D9333F, #C3D825, #2CA9E1},
{#e2be2d, #d6da1f, #22bc79, #1a75a9},
{#ff2f20, #f0762b, #f0aa3a, #219fd5},
};
color[] randomPalette() {
return palettes[(int)random(palettes.length)];
}
// https://en.wikipedia.org/wiki/Smoothstep
float smoothstep(float edge0, float edge1, float x) {
// Scale, bias and saturate x to 0..1 range
x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
// Evaluate polynomial
return x * x * (3 - 2 * x);
}
float clamp(float x, float lowerlimit, float upperlimit) {
if (x < lowerlimit)
x = lowerlimit;
if (x > upperlimit)
x = upperlimit;
return x;
}
sketch_210806a.pde
Firework[] fireworks;
int pt;
void setup() {
size(800, 800);
background(32);
fireworks = new Firework[100];
}
void draw() {
background(32);
translate(width / 2.0, height / 2.0);
scale(0.75);
for (Firework fw : fireworks) {
if (fw == null) {
break;
}
fw.update();
fw.draw();
}
}
void mousePressed() {
fireworks[pt] = new Firework(
new PVector((mouseX - width / 2.0) * 1.25, (mouseY - height / 2.0) * 1.25),
randomPalette()
);
pt = (pt + 1) % fireworks.length;
}
void keyPressed() {
saveFrame("cover.png");
}