#include <Adafruit_NeoPixel.h>
#define LED_PIN 2
#define LED_COUNT 60
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
const int MAX_PARTICLES = 60;
struct Particle {
float pos;
float vel;
uint16_t hue;
int life;
bool active;
bool isCore; // NEW: bright center flash
};
Particle particles[MAX_PARTICLES];
const float DRAG = 0.98;
const int MAX_LIFE = 60;
unsigned long lastSpawn = 0;
const unsigned long SPAWN_INTERVAL = 800;
void setup() {
strip.begin();
strip.show();
randomSeed(analogRead(0));
}
void loop() {
if (millis() - lastSpawn > SPAWN_INTERVAL) {
spawnExplosion();
lastSpawn = millis();
}
updateParticles();
drawParticles();
strip.show();
delay(20);
}
void spawnExplosion() {
int center = random(10, LED_COUNT - 10);
uint16_t baseHue = random(0, 65535);
int numParticles = random(15, 30);
// Bright center spark(s)
for (int j = 0; j < MAX_PARTICLES; j++) {
if (!particles[j].active) {
particles[j] = {
(float)center,
0.0,
baseHue,
4, // Short life
true,
true // Mark as core flash
};
break;
}
}
// Flying particles
for (int i = 0; i < numParticles; i++) {
for (int j = 0; j < MAX_PARTICLES; j++) {
if (!particles[j].active) {
float velocity = (random(-100, 100) / 100.0) * 1.5;
particles[j] = {
(float)center,
velocity,
baseHue + random(-1000, 1000),
MAX_LIFE,
true,
false
};
break;
}
}
}
}
void updateParticles() {
for (int i = 0; i < MAX_PARTICLES; i++) {
Particle &p = particles[i];
if (p.active) {
p.pos += p.vel;
p.vel *= DRAG;
p.life--;
if (p.life <= 0 || p.pos < 0 || p.pos >= LED_COUNT) {
p.active = false;
}
}
}
}
void drawParticles() {
strip.clear();
for (int i = 0; i < MAX_PARTICLES; i++) {
Particle &p = particles[i];
if (p.active) {
int index = round(p.pos);
if (index >= 0 && index < LED_COUNT) {
uint8_t brightness = p.isCore ?
255 : map(p.life, 0, MAX_LIFE, 0, 200); // brighter core
uint32_t color = strip.ColorHSV(p.hue, 255, brightness);
strip.setPixelColor(index, color);
}
}
}
}