Photos of your completed project (video optional):
This Halloween, I built a “Stray Robot” costume as a special tribute to our family. With my husband and I expecting our “second” son at the end of this year, we wanted to make this Halloween all about our “first son,” our orange tabby cat, Apu, putting him in the main spotlight. The costume is inspired by the video game Stray, which was a perfect theme since the main character is an orange tabby cat in a world populated only by robots. Our costume symbolized our deep love for Apu—we would even turn into robots to make him the star. The construction was a learning process; the head was built with an internal support and a waist belt for stability, and I taught myself how to program Arduino for the LED lights. Although it was a success, if I were to make it again, I would definitely design the head to be a bit smaller.
List (with links) of materials and parts used:
BTF-LIGHTING WS2812B RGB 5050SMD Individually Addressable Digital 16×16 256 Pixels 6.3in x 6.3in LED Matrix Flexible FPCB Dream Full Color Works with SP802E Controller Image Video Text Display DC5V
BTF-LIGHTING WS2812B IC RGB 5050SMD Pure Gold Individual Addressable LED Strip 16.4FT 300LED 60Pixel/m Flexible Full Color IP30 DC5V for DIY Chasing Color Project(No Adapter or Controller)
Cardboard
Acrylic board
Spray paints
Glass paints
Hot glue
Circuit diagram
Arduino code
// Simple demonstration on using an input device to trigger changes on your
// NeoPixels. Wire a momentary push button to connect from ground to a
// digital IO pin. When the button is pressed it will change to a new pixel
// animation. Initial state has all pixels off -- press the button once to
// start the first animation. As written, the button does not interrupt an
// animation in-progress, it works only when idle.
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// Digital IO pin connected to the button. This will be driven with a
// pull-up resistor so the switch pulls the pin to ground momentarily.
// On a high -> low transition the button press logic will execute.
#define BUTTON_PIN 2
#define PIXEL_PIN 1 // Digital IO pin connected to the NeoPixels.
//#define PIXEL_COUNT 256 // Number of NeoPixels
#define MATRIX_WIDTH 14
#define MATRIX_HEIGHT 16
#define PIXEL_COUNT (MATRIX_WIDTH * MATRIX_HEIGHT)
//#define BRIGHTNESS 10
// Define the brightness values requested
const uint8_t BACKGROUND_BRIGHTNESS = 100; // For the RGB rainbow part
const uint8_t FACE_BRIGHTNESS = 150; // For the RGB components of the white face
// // Get current button state.
// const uint8_t BACKGROUND_BRIGHTNESS = 20; // For the RGB rainbow part
// const uint8_t FACE_BRIGHTNESS = 35; // For the RGB components of the white face
const byte On_HOLDMap[MATRIX_HEIGHT][MATRIX_WIDTH] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,1,1,1,1,0,0,0,0,0,0},
{0,0,0,0,1,1,1,1,0,0,0,0,0,0}, // Eye
{0,0,0,0,1,1,1,1,0,0,1,0,0,0}, // Eye
{0,0,0,0,0,1,1,0,0,0,1,0,0,0}, // Eye
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,1,1,0,0,0,1,0,0,0}, // Mouth
{0,0,0,0,1,1,1,1,0,0,1,0,0,0}, // Mouth
{0,0,0,0,1,1,1,1,0,0,0,0,0,0},
{0,0,0,0,1,1,1,1,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
const byte Wink_Map[MATRIX_HEIGHT][MATRIX_WIDTH] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0,0,0,0,0}, // Eye
{0,0,0,0,0,1,1,0,0,0,1,0,0,0}, // Eye
{0,0,0,0,0,1,1,0,0,0,1,0,0,0}, // Eye
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,1,1,0,0,0,1,0,0,0}, // Mouth
{0,0,0,0,1,1,1,1,0,0,1,0,0,0}, // Mouth
{0,0,0,0,1,1,1,1,0,0,0,0,0,0},
{0,0,0,0,1,1,1,1,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
const byte no_words_Map[MATRIX_HEIGHT][MATRIX_WIDTH] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0,0,0,0,0}, // Eye
{0,0,0,0,0,1,1,0,0,0,1,0,0,0}, // Eye
{0,0,0,0,0,1,1,0,0,0,1,0,0,0}, // Eye
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,1,1,0,0,0,1,0,0,0}, // Mouth
{0,0,0,0,0,1,1,0,0,0,1,0,0,0}, // Mouth
{0,0,0,0,0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
const byte heartMap[MATRIX_HEIGHT][MATRIX_WIDTH] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,1,1,0,0,0,0,0,0,0,0},
{0,0,0,1,1,1,1,0,0,0,0,0,0,0}, // Bigger Heart Top
{0,0,1,1,1,1,1,1,0,0,0,0,0,0},
{0,0,1,1,1,1,1,1,1,0,0,0,0,0},
{0,0,1,1,1,1,1,1,1,1,0,0,0,0},
{0,0,0,1,1,1,1,1,1,1,1,0,0,0},
{0,0,0,0,1,1,1,1,1,1,1,1,0,0},
{0,0,0,1,1,1,1,1,1,1,1,0,0,0},
{0,0,1,1,1,1,1,1,1,1,0,0,0,0},
{0,0,1,1,1,1,1,1,1,0,0,0,0,0},
{0,0,1,1,1,1,1,1,0,0,0,0,0,0}, // Bigger Heart Tip
{0,0,0,1,1,1,1,0,0,0,0,0,0,0},
{0,0,0,0,1,1,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
boolean oldState = HIGH;
int mode = 0; // Currently-active animation mode, 0-9
int brightness = 25;
void setup() {
strip.setBrightness(brightness);
pinMode(BUTTON_PIN, INPUT_PULLUP);
strip.begin(); // Initialize NeoPixel strip object (REQUIRED)
strip.show(); // Initialize all pixels to 'off'
}
void loop() {
boolean newState = digitalRead(BUTTON_PIN);
// Check if state changed from high to low (button press).
if((newState == LOW) && (oldState == HIGH)) {
// Short delay to debounce button.
delay(20);
// Check if button is still low after debounce.
newState = digitalRead(BUTTON_PIN);
if(newState == LOW) { // Yes, still low
if(++mode > 9) mode = 0; // Advance to next mode, wrap around after #8
switch(mode) { // Start the new animation...
case 0:
colorWipe(strip.Color( 0, 0, 0), 50); // Black/off
break;
case 1:
drawPattern_on_hold_face();
break;
case 2:
drawPattern_wink_face();
//
break;
case 3:
//colorWipe(strip.Color( 0, 0, 255), 50); // Blue
drawPattern_on_hold_face();
break;
case 4:
//theaterChase(strip.Color(127, 127, 127), 50); // White
//drawCirclePattern();
drawPattern_rainbow_heart_face();
break;
case 5:
drawPattern_on_hold_face();
//theaterChase(strip.Color(127, 0, 0), 50); // Red
break;
case 6:
drawPattern_no_words_face();
//theaterChase(strip.Color( 0, 0, 127), 50); // Blue
break;
case 7:
//rainbow(10);
//drawCirclePattern();
drawPattern_heart_face();
break;
case 8:
theaterChaseRainbow(50);
break;
}
}
}
// Set the last-read button state to the old state.
oldState = newState;
}
void drawPattern_on_hold_face() {
// Iterate over every pixel in the matrix.
for (int y = 0; y < MATRIX_HEIGHT; y++) {
for (int x = 0; x < MATRIX_WIDTH; x++) {
// Get the 1D index for the pixel at (x,y) coordinates.
int pixelIndex = getPixelIndex(x, y);
// Check the bitmap to see if this pixel is part of the smiley.
if (On_HOLDMap[y][x] == 1) {
// This pixel is part of the face.
uint8_t current_brightness = FACE_BRIGHTNESS;
// Add vertical stripe effect by brightening odd-numbered columns.
if (x % 2 != 0) {
// Add brightness for the stripe, ensuring it doesn't exceed 255.
current_brightness = min(255, current_brightness + 15);
}
// Set an RGB white color by setting R, G, and B to the same value.
strip.setPixelColor(pixelIndex, strip.Color(current_brightness, current_brightness, current_brightness));
} else {
// This pixel is part of the background.
// To reverse the rainbow direction (green->yellow->red...), we start
// at the green hue (21845) and SUBTRACT a value that increases
// across the diagonal. This makes the hue decrease.
uint16_t hue = 21845 - (((uint32_t)((x - y) + (MATRIX_HEIGHT - 1)) * 65535) / (MATRIX_WIDTH + MATRIX_HEIGHT - 2));
uint8_t current_brightness = BACKGROUND_BRIGHTNESS;
// Add vertical stripe effect by brightening odd-numbered columns.
if (x % 2 != 0) {
current_brightness += 8; // Make stripes on background brighter
}
// Get the 32-bit color value from HSV (Hue, Saturation, Value/Brightness).
uint32_t color_background = strip.ColorHSV(hue, 255, current_brightness);
strip.setPixelColor(pixelIndex, color_background);
}
}
}
// After setting all the pixel colors, push the data to the strip.
strip.show();
}
void drawPattern_no_words_face() {
// Iterate over every pixel in the matrix.
for (int y = 0; y < MATRIX_HEIGHT; y++) {
for (int x = 0; x < MATRIX_WIDTH; x++) {
// Get the 1D index for the pixel at (x,y) coordinates.
int pixelIndex = getPixelIndex(x, y);
// Check the bitmap to see if this pixel is part of the smiley.
if (no_words_Map[y][x] == 1) {
// This pixel is part of the face.
uint8_t current_brightness = FACE_BRIGHTNESS;
// Add vertical stripe effect by brightening odd-numbered columns.
if (x % 2 != 0) {
// Add brightness for the stripe, ensuring it doesn't exceed 255.
current_brightness = min(255, current_brightness + 15);
}
// Set an RGB white color by setting R, G, and B to the same value.
strip.setPixelColor(pixelIndex, strip.Color(current_brightness, current_brightness, current_brightness));
} else {
// This pixel is part of the background.
// To reverse the rainbow direction (green->yellow->red...), we start
// at the green hue (21845) and SUBTRACT a value that increases
// across the diagonal. This makes the hue decrease.
uint16_t hue = 21845 - (((uint32_t)((x - y) + (MATRIX_HEIGHT - 1)) * 65535) / (MATRIX_WIDTH + MATRIX_HEIGHT - 2));
uint8_t current_brightness = BACKGROUND_BRIGHTNESS;
// Add vertical stripe effect by brightening odd-numbered columns.
if (x % 2 != 0) {
current_brightness += 8; // Make stripes on background brighter
}
// Get the 32-bit color value from HSV (Hue, Saturation, Value/Brightness).
uint32_t color_background = strip.ColorHSV(hue, 255, current_brightness);
strip.setPixelColor(pixelIndex, color_background);
}
}
}
// After setting all the pixel colors, push the data to the strip.
strip.show();
}
void drawPattern_wink_face() {
// Iterate over every pixel in the matrix.
for (int y = 0; y < MATRIX_HEIGHT; y++) {
for (int x = 0; x < MATRIX_WIDTH; x++) {
// Get the 1D index for the pixel at (x,y) coordinates.
int pixelIndex = getPixelIndex(x, y);
// Check the bitmap to see if this pixel is part of the smiley.
if (Wink_Map[y][x] == 1) {
// This pixel is part of the face.
uint8_t current_brightness = FACE_BRIGHTNESS;
// Add vertical stripe effect by brightening odd-numbered columns.
if (x % 2 != 0) {
// Add brightness for the stripe, ensuring it doesn't exceed 255.
current_brightness = min(255, current_brightness + 15);
}
// Set an RGB white color by setting R, G, and B to the same value.
strip.setPixelColor(pixelIndex, strip.Color(current_brightness, current_brightness, current_brightness));
} else {
// This pixel is part of the background.
// To reverse the rainbow direction (green->yellow->red...), we start
// at the green hue (21845) and SUBTRACT a value that increases
// across the diagonal. This makes the hue decrease.
uint16_t hue = 21845 - (((uint32_t)((x - y) + (MATRIX_HEIGHT - 1)) * 65535) / (MATRIX_WIDTH + MATRIX_HEIGHT - 2));
uint8_t current_brightness = BACKGROUND_BRIGHTNESS;
// Add vertical stripe effect by brightening odd-numbered columns.
if (x % 2 != 0) {
current_brightness += 8; // Make stripes on background brighter
}
// Get the 32-bit color value from HSV (Hue, Saturation, Value/Brightness).
uint32_t color_background = strip.ColorHSV(hue, 255, current_brightness);
strip.setPixelColor(pixelIndex, color_background);
}
}
}
// After setting all the pixel colors, push the data to the strip.
strip.show();
}
void drawPattern_heart_face() {
// Iterate over every pixel in the matrix.
for (int y = 0; y < MATRIX_HEIGHT; y++) {
for (int x = 0; x < MATRIX_WIDTH; x++) {
// Get the 1D index for the pixel at (x,y) coordinates.
int pixelIndex = getPixelIndex(x, y);
// Check the bitmap to see if this pixel is part of the heart.
if (heartMap[y][x] == 1) {
// This pixel is part of the heart.
uint8_t current_brightness = FACE_BRIGHTNESS;
// Add vertical stripe effect by brightening odd-numbered columns.
if (x % 2 != 0) {
// Add brightness for the stripe, ensuring it doesn't exceed 255.
current_brightness = min(255, current_brightness + 15);
}
// Set an RGB red color.
strip.setPixelColor(pixelIndex, strip.Color(current_brightness, 0, 0));
} else {
// This pixel is part of the background.
// Set a static sky blue color.
uint16_t sky_blue_hue = 36000; // A nice cyan/sky blue hue.
uint8_t current_brightness = BACKGROUND_BRIGHTNESS;
// Add vertical stripe effect by brightening odd-numbered columns.
if(x == 0 && y == 0){
Serial.print("before: ");
Serial.println(current_brightness);
}
// if (x % 2 == 0) {
// current_brightness += 8; // Make stripes on background brighter
// }
if(x == 0 && y == 0){
Serial.print("after: ");
Serial.println(current_brightness);
}
// Get the 32-bit color value from HSV (Hue, Saturation, Value/Brightness).
uint32_t color_background = strip.ColorHSV(sky_blue_hue, 255, 255);
strip.setPixelColor(pixelIndex, color_background);
}
}
}
// After setting all the pixel colors, push the data to the strip.
strip.show();
}
void drawPattern_rainbow_heart_face() {
// Iterate over every pixel in the matrix.
for (int y = 0; y < MATRIX_HEIGHT; y++) {
for (int x = 0; x < MATRIX_WIDTH; x++) {
// Get the 1D index for the pixel at (x,y) coordinates.
int pixelIndex = getPixelIndex(x, y);
// Check the bitmap to see if this pixel is part of the smiley.
if (heartMap[y][x] == 1) {
// This pixel is part of the face.
uint8_t current_brightness = FACE_BRIGHTNESS;
// Add vertical stripe effect by brightening odd-numbered columns.
if (x % 2 != 0) {
// Add brightness for the stripe, ensuring it doesn't exceed 255.
current_brightness = min(255, current_brightness + 15);
}
// Set an RGB white color by setting R, G, and B to the same value.
strip.setPixelColor(pixelIndex, strip.Color(current_brightness, current_brightness, current_brightness));
} else {
// This pixel is part of the background.
// To reverse the rainbow direction (green->yellow->red...), we start
// at the green hue (21845) and SUBTRACT a value that increases
// across the diagonal. This makes the hue decrease.
uint16_t hue = 21845 - (((uint32_t)((x - y) + (MATRIX_HEIGHT - 1)) * 65535) / (MATRIX_WIDTH + MATRIX_HEIGHT - 2));
uint8_t current_brightness = BACKGROUND_BRIGHTNESS;
// Add vertical stripe effect by brightening odd-numbered columns.
if (x % 2 != 0) {
current_brightness += 8; // Make stripes on background brighter
}
// Get the 32-bit color value from HSV (Hue, Saturation, Value/Brightness).
uint32_t color_background = strip.ColorHSV(hue, 255, current_brightness);
strip.setPixelColor(pixelIndex, color_background);
}
}
}
// After setting all the pixel colors, push the data to the strip.
strip.show();
}
void drawCirclePattern() {
// Define the colors we will use.
uint32_t color_circle = strip.Color(255, 255, 255); // White
uint32_t color_background = strip.Color(255, 0, 0); // Green
// Define the center of the matrix (using float for accuracy).
float centerX = (MATRIX_WIDTH - 1) / 2.0f;
float centerY = (MATRIX_HEIGHT - 1) / 2.0f;
// Define the radius of the circle.
float radius = 1.0f;
// This value controls the thickness of the circle's line.
float thickness = 1.0f;
// Iterate over every pixel in the matrix.
for (int y = 0; y < MATRIX_HEIGHT; y++) {
for (int x = 0; x < MATRIX_WIDTH; x++) {
// Calculate the distance of the current pixel (x, y) from the center.
float distance = sqrt(pow(y - centerY, 2) + pow(x - centerX, 2));
// Get the 1D index for the pixel at (x,y) coordinates.
int pixelIndex = getPixelIndex(x, y);
// Check if the distance is within the circle's line.
if (fabs(distance - radius) < thickness) {
// This pixel is part of the circle, set it to white.
strip.setPixelColor(pixelIndex, color_circle);
} else {
// This pixel is part of the background, set it to green.
strip.setPixelColor(pixelIndex, color_background);
}
}
}
// After setting all the pixel colors in memory, push the data to the strip.
strip.show();
}
int getPixelIndex(int x, int y) {
if (y % 2 == 0) {
// Even row: 0, 2, 4,...
return y * MATRIX_WIDTH + x;
} else {
// Odd row: 1, 3, 5,...
return y * MATRIX_WIDTH + (MATRIX_WIDTH - 1 - x);
}
}
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
for(int a=0; a<10; a++) { // Repeat 10 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
// Hue of first pixel runs 3 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 3*65536. Adding 256 to firstPixelHue each time
// means we'll make 3*65536/256 = 768 passes through this outer loop:
for(long firstPixelHue = 0; firstPixelHue < 3*65536; firstPixelHue += 256) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
// Offset pixel hue by an amount to make one full revolution of the
// color wheel (range of 65536) along the length of the strip
// (strip.numPixels() steps):
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
// strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
// optionally add saturation and value (brightness) (each 0 to 255).
// Here we're using just the single-argument hue variant. The result
// is passed through strip.gamma32() to provide 'truer' colors
// before assigning to each pixel:
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for(int a=0; a<30; a++) { // Repeat 30 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / strip.numPixels();
uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.setBrightness(10);
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
My costume, “Ghost Doll Cat,” represents both comfort and invisibility — it’s a little spooky but also silly and cute. I wanted to be like a real plush cat doll: quietly sitting in the corner, sometimes calm, sometimes playful, always just being myself.
I designed this costume as a kind of soft shield — something that hides me, yet lets me join everyone at the same time. The glowing fishbone pillow adds a bit of humor and life to the ghostly form. I chose a cat because cats always do whatever they want — even as ghosts, they remain mysterious and independent.
How to wear
To wear the costume, locate it through the red eyes first and adjust the ear positions. Each time it fits slightly differently, and the feeling varies depending on the person’s body shape under the cover, which I find very interesting.
What I learn
I learned how to use an electric embroidery machine — it was so much fun (though a bit expensive), and it made me more thoughtful about my design patterns. I also learned how to set up the Gemma board and connected the NeoPixel light strips.
What I would do differently
If I could remake it, I’d probably make the eye area smaller and use a more transparent fabric. Although I can still see people through the cloth, it’s not very convenient to move around. Next time, I’d also add an elastic band around the neck to create a Sunnyday Doll “teru teru bozu” style doll shape — to make it look even creepier and more doll-like.
materials
White fabric (photography backdrop) – main ghost part (https://a.co/d/fiu8FMx) Black fabric (thick curtain) – fishbone cushion (https://a.co/d/iN3wOLF) Red fabric (translucent) – eyes White embroidery thread and white ribbon – fishbone pattern (https://a.co/d/jlelgSO) Black embroidery thread and black marker – eyes Neo pixel x 99, one Gemma, soldering wire (https://a.co/d/a3CFcol)
I still prefer my first idea — cosplaying as Plankton’s electronic wife, Karen, from SpongeBob SquarePants. Although my second idea, the angel wings, is visually beautiful, it doesn’t feel as conceptually interesting. However, wearing a large “computer” on my head during the Halloween parade would not be safe. So I’m thinking about transforming the computer into a handheld screen or a portable box, while dressing myself in a mechanical or cyber-inspired outfit.
This way, I can ensure safety during the parade, and when taking photos, I can lift the screen up to my face to recreate Karen’s look — it would still be visually striking and conceptually playful.
1.Structure & Function
The device functions as a portable electronic screen that displays animated graphics and patterns. A button allows the user to switch between multiple display modes (heartbeat line, smile, or flat signal), recreating the reactive visual behavior of Karen from SpongeBob SquarePants.
2.Materials
Main Board:Use an Arduino UNO as the main control board to manage lighting and button interactions.
Display Module:BTF-LIGHTING WS2812B IC RGB 5050SMD Pure Gold Individual Addressable LED Strip 16.4FT 300LED 60Pixel/m Flexible Full Color IP30 DC5V for DIY Chasing Color Project
Light Source:Apply green LEDs or RGB lights (green channel only) to recreate Karen’s iconic electronic green glow.
Button:Add a small push button to switch between different facial expressions or animation modes.
I’ve decided to move ahead with my halloween costume resembling Eve from Pixar’s Wall-E. I’m particularly interested in Eve’s vocabulary of moods as demonstrated through her different eye-states. This is an important part of her character building in Wall-E as we see her character go from a mindless mission driven robot to the considerate and caring character that audiences love.
To tackle these different mood-states I’ve decided to use an LED matrix for each of Eve’s eyes and glue them to the inside of a full-face sunglass visor. Below are a few examples of different mood states I could express via the LED matrix. It’s particularly useful in this case that I may be able to use different colors through the RGB matrix to express enhanced states like anger.
I think I will mostly have a loop of alert with a ‘blinking’ animation loop to give the eyes effect. I will include a button on the visor or battery pack to toggle between mood states.
Shopping List
2x 8×8 RGB LED Matrix (Reminder to ask Becky which is best) 1x Sunglasses Visor 1x Rubber Gasket (to create distance between the visor + matrix and the face) 1x White Headcover 1x Adafruit Gemma 1x Battery Pack 1x Tactile Switch Button __x Jumper Wire __x Heat Seal Gasket __x Glue (Reminder to ask Becky which is best)
To-do list: 3D Print the Gem – option 1 – option 2 Solder the Led Strip – Attach to inside of pole? Attach Gem to Pole Wrap pole with fur to create handle Tie ribbons on to the pole
Arduino: I want to use a button to maybe toggle between two settings, both will be a fade through some gradient of pink light. I want to confirm, the best way to go about this.
This project recreates the iconic “?” Question Box from the Super Mario video game series as an interactive prop. When someone punches or taps the bottom of the box, it lights up brightly and plays a Mario coin sound, just like in the game when Mario collects a coin or power-up.
The intention behind this costume prop is to bring nostalgia and fun to Halloween. It’s not designed to scare people — it’s designed to make people smile and feel like they’re inside the Mario world. When friends see the glowing box and hear the coin sound, they immediately recognize it and often want to “hit” it themselves! It’s both interactive and playful, perfect for a group or gaming-themed costume.
Concept: Inspired by the iconic Question Block from the Super Mario games, this interactive box lights up and changes color whenever it’s “hit” from below — just like in the game when Mario punches it to release a coin or power-up. The project uses NeoPixel LEDs for vivid lighting effects and an impact sensor (or push button) at the bottom to detect the punch or tap.
Objective: To recreate a nostalgic video game interaction using Arduino and NeoPixel LEDs, demonstrating how sensors can trigger real-time lighting animations.
Step-by-Step Build Process
1️⃣ Prototype on Breadboard
Connect Arduino, DFPlayer, button, and LED strip on breadboard.
Upload code to test light + sound reaction.
2️⃣ Prepare the Box
Build or purchase a yellow box (15–20 cm cube).
Cut question marks (white “?”) from paper or vinyl.
Mount LED strip inside the box edges (with the arrows following data flow).
3️⃣ Install Components
Fix Arduino + DFPlayer inside the bottom compartment.
Mount the button under the bottom panel (acts as the “hit detector”).
Place the speaker facing downward through a small hole for louder sound.
Secure all wiring with hot glue or Velcro.
4️⃣ Power and Testing
Connect USB power bank → Arduino’s USB port.
Press the bottom to test:
LEDs flash and shift colors ✨
Coin sound plays 🔊
5️⃣ Adjust Brightness & Volume
🧠 Physical Construction
Box Design
Construct a cube (around 15–20 cm) using yellow cardboard, acrylic, or foam board.
Add white “?” symbols on all sides for the authentic Mario look.
Install NeoPixel LEDs inside the box so the light diffuses softly through the walls.
The bottom panel will house the button or impact sensor — users can punch or tap it to activate the light show.
Electronics Placement
Place the Arduino Nano inside or beneath the box.
Wires from the impact sensor and LEDs feed into the Arduino through small holes.
The power supply (battery pack) can sit outside or be hidden beneath the display stand.
I added a sparkle animation to the Arduino code. Here is an explaination below:
uint32_t c This animation starts the function by telling the Arduino to output color across a range of RGBW. As this represents 4 different colors with their respective intensities, this information has to be stored within a large enough range. As color intensity varies from 0-255, the color parameter requires a 32-bit parameter in order to account for the 0-255 range for each color (RGBW). This is because 32-bits represents 256 different combinations of 1s and 0s, for all 4 colors RGBW
uint8_t wait This animation then defines the ‘wait’ time as an 8-bit function — meaning it has the capacity to count to 256 milliseconds (2^8th power). Using an 8-bit variable here saves a few bits of storage and is particulary useful in this case as the pauses between the loop running are very short within the 0 -256 millisecond range. *I found this part particularly interesting as I’ve learned about quartz watches in the past and I wanted to know how this relates.
strip.clear(); This clears the strip of any colors/brightness before the wipe begins
strip.setPixelColor(random(NUM_LEDS), c); This defines the random value that will be assigned to any of the 4 LEDs in this chain. We know there are 4 LEDs because we’ve pre-defined it for our variable NUM_LEDS
strip.show(); The color isn’t pushed to an LED until this ‘show’ line is run.
delay(wait); There is a delay before the loop is run again. This delay is defined by the 100 in the following line sparkle(strip.Color(255, 255, 255), 100);
Halloween Costume Ideas
Daft Punk (MTA Edition)
Daft Punk is back except its not all fun and dancing, its practical MTA train times so you never miss the F again! I could implement a LED strip across the 3D printed visor that would allow light animations to sweep over the screen to display text. With this I may want to change the helmet just slightly to make it more my own and less a replica.
Eve Glasses
In Pixar’s Wall-E (one of the top sci films of this century) Eve’s eyes squint up when she laughs. I thought this is a cool feature to implement into glasses that could be a main piece of a halloween costume. Perhaps the glasses have a gyroscope to squint when the head tilts up.
NYC Crosswalk Sign
Another idea is to have a shirt which functions like the NYC crosswalk. Perhaps a custom animation that is a bit different or unique from the NYC signs could give it an extra kick. Or maybe it walks when I walk and then the stop hand comes up when I stop moving.
I decided to find inspiration in the styling of some characters I’ve always really enjoyed + I know that I want to make something using acrylic sheets, the way the lights look within the transparent frame feels very enticing.
Ice Headpiece This is the ice queen from the early 2000s movie sharkboy and lavagirl. I’ve always been drawn to the world they created around her, the headpiece is actively the cherry on top. I would love to experiment with this concept and make some sort of pierced acrylic headpiece.
Shopping List: Acrylic sheets Metal hoops/accessories Headband as base laser cutting appointment
Steven Universe Spinel Spinel is a scorned character in Steven Universe, the way they designed her character to reflect the arc of her character has always been so satisfying to me. a. I want to make the axe that Spinel carries. theres a red Spinel gem in the middle of a transparent/translucent “blade”. I’m curious of playing with this design and making a glowing heart scepter. b. Spinel also has these heart shaped hair puffs. Im curious about making a headband in the shape of a heart, that could possible be covered with my hair or just worn as a headband.
Shopping List: some sort of staff/decorated stick fabric to wrap handle acrylic sheets laser cutting appointment
Ideation1:Squid Game Mask – Interactive NeoPixel LED Costume
Concept: This interactive Squid Game mask merges pop culture with creative engineering. The square LED frame on the front of the mask is made of NeoPixel LEDs controlled by an Arduino board. Each LED is individually addressable, allowing dynamic lighting effects such as color transitions, breathing effects, and rainbow animations. The wearer can switch between lighting modes using a small push button hidden inside or behind the mask.
Objective: To create a visually stunning, programmable Halloween mask that demonstrates Arduino’s control over addressable LEDs, while preserving the iconic Squid Game guard design.
Concept: This Super Mario Question Block — when you hit the bottom of the box, it lights up, sparkles, and changes color just like in the game. It’s powered by an Arduino Uno and NeoPixel LED strip, using a push button (or vibration sensor) to detect when the box is struck.
Objective: To build an interactive object that uses Arduino Uno to control NeoPixel lighting effects triggered by physical input — combining creativity, electronics, and game-inspired design.
Ideation3:Glowing Stick Figure Costume with Dynamic Expression Face
Concept: Combine with wearable technology and creative expression. The costume transforms the wearer into a glowing stick figure — every limb is outlined with addressable NeoPixel LEDs controlled by an Arduino Uno. The head features a programmable digital face screen, capable of displaying different facial expressions such as happy, angry, or surprised.
Users can press a button or use a sensor to switch between expressions, giving the illusion of a living, reactive digital character.
Objective: To design a full-body interactive costume that demonstrates both LED control and real-time facial animation using Arduino Uno and a small display module.
This is my final Halloween creation: a sophisticated wizard hat with exquisite detailing.The hat is adorned with intricate spider webs made from shimmering diamonds, complemented by a large red bow at the front. Soft red tulle, embellished with sequins, gently flows around the brim, while black lace adds an elegant finish.
The hat features a wide brim that offers both style and balance, with tassels hanging down to add a touch of mystery. It’s an ideal choice for anyone looking to elevate their Halloween look with a touch of magic and refinement.
For the design, I chose a combination of three colors—orange, purple, and red—and set them to alternate in a flashing pattern. I believe these colors complement my work perfectly, creating a dynamic and visually appealing effect.
Process and Materials List:
ScreenshotScreenshotScreenshot
Black lace, red and black felt, red shimmery gauze, crystal tassel.
Lighting Effects:
#include <Adafruit_NeoPixel.h>
#define PIN 1
#define NUM_LEDS 8
#define BRIGHTNESS 50
// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
// NEO_RGB Pixels are wired for RGB bitstream
// NEO_GRB Pixels are wired for GRB bitstream, correct if colors are swapped upon testing
// NEO_RGBW Pixels are wired for RGBW bitstream
// NEO_KHZ400 400 KHz bitstream (e.g. FLORA pixels)
// NEO_KHZ800 800 KHz bitstream (e.g. High Density LED strip), correct for neopixel stick
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRBW + NEO_KHZ800);
void setup() {
strip.setBrightness(BRIGHTNESS);
strip.begin();
strip.show(); // Initialize all pixels to 'off'
}
void loop() {
// Alternate fading between blue and purple
fadeColor(strip.Color(255, 0, 0)); // Red
fadeColor(strip.Color(128, 0, 128)); // Purple
fadeColor(strip.Color(255, 165, 0)); // Orange
}
// Function to fade in and out a specified color
void fadeColor(uint32_t color) {
// Gradually increase brightness
for (int brightness = 0; brightness <= 255; brightness += 5) {
setStripColor(color, brightness);
delay(30); // Adjust delay for slower or faster fade
}
// Gradually decrease brightness
for (int brightness = 255; brightness >= 0; brightness -= 5) {
setStripColor(color, brightness);
delay(30); // Adjust delay for slower or faster fade
}
}
// Function to set all LEDs to a specific color with specified brightness
void setStripColor(uint32_t color, int brightness) {
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
// Apply brightness scaling
r = (r * brightness) / 255;
g = (g * brightness) / 255;
b = (b * brightness) / 255;
// Set color for all LEDs
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(r, g, b));
}
strip.show();
}
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
void pulseWhite(uint8_t wait) {
for(int j = 0; j < 256 ; j++){
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(0,0,0, j ) );
}
delay(wait);
strip.show();
}
for(int j = 255; j >= 0 ; j--){
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(0,0,0, j ) );
}
delay(wait);
strip.show();
}
}
void rainbowFade2White(uint8_t wait, int rainbowLoops, int whiteLoops) {
float fadeMax = 100.0;
int fadeVal = 0;
uint32_t wheelVal;
int redVal, greenVal, blueVal;
for(int k = 0 ; k < rainbowLoops ; k ++){
for(int j=0; j<256; j++) { // 5 cycles of all colors on wheel
for(int i=0; i< strip.numPixels(); i++) {
wheelVal = Wheel(((i * 256 / strip.numPixels()) + j) & 255);
redVal = red(wheelVal) * float(fadeVal/fadeMax);
greenVal = green(wheelVal) * float(fadeVal/fadeMax);
blueVal = blue(wheelVal) * float(fadeVal/fadeMax);
strip.setPixelColor( i, strip.Color( redVal, greenVal, blueVal ) );
}
//First loop, fade in!
if(k == 0 && fadeVal < fadeMax-1) {
fadeVal++;
}
//Last loop, fade out!
else if(k == rainbowLoops - 1 && j > 255 - fadeMax ){
fadeVal--;
}
strip.show();
delay(wait);
}
}
delay(500);
for(int k = 0 ; k < whiteLoops ; k ++){
for(int j = 0; j < 256 ; j++){
for(uint16_t i=0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(0,0,0, j ) );
}
strip.show();
}
delay(2000);
for(int j = 255; j >= 0 ; j--){
for(uint16_t i=0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(0,0,0, j ) );
}
strip.show();
}
}
delay(500);
}
void whiteOverRainbow(uint8_t wait, uint8_t whiteSpeed, uint8_t whiteLength ) {
if(whiteLength >= strip.numPixels()) whiteLength = strip.numPixels() - 1;
int head = whiteLength - 1;
int tail = 0;
int loops = 3;
int loopNum = 0;
static unsigned long lastTime = 0;
while(true){
for(int j=0; j<256; j++) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
if((i >= tail && i <= head) || (tail > head && i >= tail) || (tail > head && i <= head) ){
strip.setPixelColor(i, strip.Color(0,0,0, 255 ) );
}
else{
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
}
if(millis() - lastTime > whiteSpeed) {
head++;
tail++;
if(head == strip.numPixels()){
loopNum++;
}
lastTime = millis();
}
if(loopNum == loops) return;
head%=strip.numPixels();
tail%=strip.numPixels();
strip.show();
delay(wait);
}
}
}
void fullWhite() {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(0,0,0, 255 ) );
}
strip.show();
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256 * 5; j++) { // 5 cycles of all colors on wheel
for(i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}
void rainbow(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) {
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i+j) & 255));
}
strip.show();
delay(wait);
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3,0);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3,0);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0,0);
}
uint8_t red(uint32_t c) {
return (c >> 8);
}
uint8_t green(uint32_t c) {
return (c >> 16);
}
uint8_t blue(uint32_t c) {
return (c);
}