Front view of party button with power at resting state of red loading lights.
This project is a party button meant to help celebrate the smaller wins in life, making them feel more momentous. The party button can be pressed to trigger a celebratory sequence, including a fun light show, music, and a small confetti popper.
Side view of the party button showing a detailed confetti launcher mechanism: a DC motor with a rotating pin that catches and pulls a string attached to the confetti popper, triggering its release.
Close-up of the speaker grille in a dimly lit setting, highlighting the glow of the light strip around the outside of the device.
Top view of the project showing the central button surrounded by a ring of illuminated LEDs. In the corner, the opening for the confetti launcher is also visible.
Party button in its resting state while plugged in, displaying a red loading light.
First run of the party button being pressed after full assembly.
This video shows the final run of the party button sequence when pressed, including the confetti launch and light effects. (Turn up the volume to hear the music.)
Modeling the space in the housing for the confetti popper. This shows the measured dimensions of the popper as a section that was then revolved.
Wiering setup before being housed.
DC motor being used in party button.
Servo motor and quick string pull mechanism prototype. (This was too weak to pull the string of the popper so ened up using the DC motor that used an external power source)
small scale test of the confetrtie popper withour being in full project housing.
light and music test 1
light and music test 2
Wiring setup with confetti popper attached the a servo motor.
testing the loading light movment to swirl and fade red lights around the neopixle ring.
When creating and designing this device, I chose to approach it by first figuring out all of the coding and electrical components. I tested the lights, then the sound, and finally the confetti mechanism. After confirming that each component worked individually, I integrated everything into the housing.
I was able to experiment extensively with the light colors and also decided to create a custom audio file for the music. For the confetti mechanism, I initially built a small-scale version to hold the popper and test its functionality. Once that was successful, I moved on to designing the full housing.
While designing the housing, I intentionally included space for the lighting system as well as diffusing covers to enhance the overall visual effect.
Two of the main comments I got during the in-class critique were about the confetti popper. One person wrote, “Maybe making the popper more prominent would be nice.” I agree with this because the confetti is a big feature of the device and could be highlighted more intentionally. At the same time, I do like that there is some surprise to it, especially for a first-time user. One idea that came up was having the popper slowly rise before it goes off, which I think would be a really nice way to signal that it is there while still building anticipation. I think that would help show it off more as a feature without fully giving it away. Another comment was to “simplify the process of replacing the popper.” I agree that it could be simpler, but I still want it to feel like a manual and intentional interaction. Right now, the lacing and knotting of the string is a bit finicky, so refining that would help a lot, but I don’t want to fully automate it and remove that hands-on aspect of loading the confetti.
Overall, I am happy with how the project turned out. I think it really achieved my goal of creating a tiny celebration. I am especially satisfied that I got all the components working both individually and together in sync. That said, I do think the final form could have been pushed further. I would have liked to spend more time exploring the shape and getting it to a higher level of fidelity. I also ran into some issues with the power system, since I had separate power sources for the motor and everything else. In the future, I would want to unify that and make the housing cleaner and more cohesive. Even with those issues, I think it still captures the whimsical “party button” idea I was going for.
From this project, I learned that I tend to focus on getting all the technical pieces working before really thinking about the form. It felt more natural to solve the functionality first and then move into aesthetics. I also found that breaking the project into smaller parts made it much easier to get started, since I was pretty stuck at the beginning. While I really enjoyed getting the electronics to work, I think I had the most fun when I was designing the form and focusing on small details. For example, figuring out how to diffuse the light around the edge and the button was really satisfying and made a big difference in the final feel.
If I were to do another iteration, I would first fix the power system so that everything runs off one source. I would also make a more refined and structurally sound housing, so it could handle someone really slamming the button without collapsing. I also think it would be really cool to explore ways for users to upload their own party music and customize the lights. Those changes would make the device feel more polished and more personal.
/*
Party Button
The party button plays music, does a fun light show, and sets off a confetti
popper using a DC motor when a button is pressed, and triggers the sequence.
This device uses an Adafruit music shield, two speakers, a button, a DC motor,
a 24-light RGBW neopixel light ring, and an RGB LED strip.
pin mapping:
Arduino pin | role | details
------------------------------
2 input button
5 output DC motor
8 output NeoPixle ring
9 output LED strip
13 output music maker shield
12 output music maker shield
11 output music maker shield
7 output music maker shield
6 output music maker shield
4 output music maker shield
3 output music maker shield
Sophie Heap, sheap@andrew.cmu.edu
*/
#include <SPI.h>
#include <Adafruit_VS1053.h>
#include <SD.h>
#include <Adafruit_NeoPixel.h>
#include <Servo.h>
// ---------------- VS1053 SETUP ----------------
#define SHIELD_RESET -1
#define SHIELD_CS 7 // VS1053 CS
#define SHIELD_DCS 6
#define DREQ 3
#define CARDCS 4
// ---------------- BUTTON ----------------
#define BUTTON_PIN 2
// ---------------- NEOPIXELS ----------------
#define RING_PIN 8
#define STRIP_PIN 9
#define NUMPIXELS_RING 24
#define NUMPIXELS_STRIP 40
// ---------------- DC MOTOR ----------------
#define MOTOR_PIN 5 // Arduino pin to control transistor/MOSFET
bool motorRunning = false;
unsigned long motorStartTime = 0;
// VS1053 player object
Adafruit_VS1053_FilePlayer musicPlayer(SHIELD_RESET, SHIELD_CS, SHIELD_DCS, DREQ, CARDCS);
// NeoPixel objects
Adafruit_NeoPixel ring(NUMPIXELS_RING, RING_PIN, NEO_GRBW + NEO_KHZ800);
Adafruit_NeoPixel strip(NUMPIXELS_STRIP, STRIP_PIN, NEO_GRB + NEO_KHZ800);
int position = 0;
void setup() {
Serial.begin(9600);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(MOTOR_PIN, OUTPUT); // control pin for motor
digitalWrite(MOTOR_PIN, LOW); // motor off at start
// ----- VS1053 -----
if (!musicPlayer.begin()) {
Serial.println("Couldn't find VS1053!");
while(1);
}
Serial.println("VS1053 ready!");
if (!SD.begin(CARDCS)) {
Serial.println("SD card failed or not present");
while(1);
}
Serial.println("SD card ready!");
musicPlayer.setVolume(0, 0); // Loudest
musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);
// ----- NeoPixels -----
ring.begin(); ring.show();
strip.begin(); strip.show();
}
void loop() {
// Check if button pressed
if (digitalRead(BUTTON_PIN) == LOW) {
// Start music (non-blocking)
musicPlayer.startPlayingFile("/TRACK002.MP3");
// --- Start motor ---
digitalWrite(MOTOR_PIN, HIGH);
motorStartTime = millis();
motorRunning = true;
Serial.println("Button pressed! Playing music and lights...");
// Light show duration (match song length or set time)
unsigned long startTime = millis();
unsigned long showDuration = 17000; // 18 seconds light show
while (millis() - startTime < showDuration) {
// ---- Motor auto shutoff after 2 seconds ----
if (motorRunning && millis() - motorStartTime >= 500) {
digitalWrite(MOTOR_PIN, LOW);
motorRunning = false;
Serial.println("Motor OFF after 2 seconds");
}
rainbowFlash();
spiralRainbow(2, 10);
}
// Wait for music to finish if still playing
while(musicPlayer.playingMusic) {
// Could add idle lights here if desired
delay(50);
}
Serial.println("Playback and light show finished.");
// Wait for button release to prevent retrigger
while(digitalRead(BUTTON_PIN) == LOW) { delay(10); }
}
// Idle red fade trail
redFadeTrail();
}
// ---- Idle red fading trail ----
// ---- Smooth idle red fade trail (brightness 10-255) ----
void redFadeTrail() {
static float phase = 0; // continuous phase for smooth movement
float speed = 0.2; // adjust for faster/slower movement
int minBrightness = 10; // minimum brightness
int maxBrightness = 255; // maximum brightness
for(int i = 0; i < NUMPIXELS_RING; i++){
float angle = (i * 2 * PI / NUMPIXELS_RING) + phase;
int brightness = (sin(angle) * 0.5 + 0.5) * (maxBrightness - minBrightness) + minBrightness;
ring.setPixelColor(i, brightness, 0, 0, 0);
}
ring.show();
for(int i = 0; i < NUMPIXELS_STRIP; i++){
float angle = (i * 2 * PI / NUMPIXELS_STRIP) + phase;
int brightness = (sin(angle) * 0.5 + 0.5) * (maxBrightness - minBrightness) + minBrightness;
strip.setPixelColor(i, brightness, 0, 0);
}
strip.show();
phase += speed;
if(phase > 2 * PI) phase -= 2 * PI; // wrap phase continuously
delay(30); // smaller = smoother
}
// ---- Flash rainbow ----
void rainbowFlash() {
uint32_t ringColors[] = {
ring.Color(255, 0, 0, 0),
ring.Color(255, 255, 0, 0),
ring.Color(0, 255, 0, 0),
ring.Color(0, 0, 255, 0),
ring.Color(180, 0, 255, 0),
ring.Color(255, 0, 120, 0)
};
uint32_t stripColors[] = {
strip.Color(255, 0, 0),
strip.Color(255, 255, 0),
strip.Color(0, 255, 0),
strip.Color(0, 0, 255),
strip.Color(180, 0, 255),
strip.Color(255, 0, 120)
};
for(int i=0; i<6; i++){
ring.fill(ringColors[i]);
strip.fill(stripColors[i]);
ring.show();
strip.show();
delay(150);
ring.clear();
strip.clear();
ring.show();
strip.show();
delay(150);
}
}
// ---- Spiral rainbow ----
void spiralRainbow(int rotations, int shiftDelay){
for(int r=0; r<rotations; r++){
for(int i=0; i<NUMPIXELS_RING; i++){
for(int j=0; j<NUMPIXELS_RING; j++){
int colorIndex = (j + i) % NUMPIXELS_RING;
ring.setPixelColor(j, Wheel((colorIndex * 256 / NUMPIXELS_RING) & 255));
}
for(int j=0; j<NUMPIXELS_STRIP; j++){
int colorIndex = (j + i) % NUMPIXELS_STRIP;
strip.setPixelColor(j, WheelRGB((colorIndex * 256 / NUMPIXELS_STRIP) & 255));
}
ring.show();
strip.show();
delay(shiftDelay);
}
}
}
// ---- Color wheels ----
uint32_t Wheel(byte WheelPos){
WheelPos = 255 - WheelPos;
if(WheelPos < 85) return ring.Color(255 - WheelPos*3, 0, WheelPos*3, 0);
else if(WheelPos < 170){ WheelPos -= 85; return ring.Color(0, WheelPos*3, 255 - WheelPos*3, 0); }
else { WheelPos -= 170; return ring.Color(WheelPos*3, 255 - WheelPos*3, 0, 0); }
}
uint32_t WheelRGB(byte WheelPos){
WheelPos = 255 - WheelPos;
if(WheelPos < 85) return strip.Color(255 - WheelPos*3, 0, WheelPos*3);
else if(WheelPos < 170){ WheelPos -= 85; return strip.Color(0, WheelPos*3, 255 - WheelPos*3); }
else { WheelPos -= 170; return strip.Color(WheelPos*3, 255 - WheelPos*3, 0); }
}