Jimmy’s Final Halloween Costume

The Plague Doctor

My Halloween costume is composed of three main parts: a 3D-printed white PLA crow mask, a black cape with a hood, and a set of three mechanical finger claws. The mask takes most of the technical work. I first used the Polycam app to scan my head and modeled the crow form around it so it would fit perfectly.

Each eye has a 12-pixel NeoPixel ring, friction-fit into an indent with wiring channels and openings for two frosted acrylic donut-shaped plates that diffuse the light. A small side button placed near my temple switches between two light patterns—a loading animation and a red glowing one.

All electronics sit inside the mask, with the wiring taped neatly along the inner surface. Felt pads and EVA foam along the edges add comfort where it touches the face, and an elastic headband—stitched through pre-modeled holes—keeps it secure. The circuit board itself is sewn to the back of the band, making everything self-contained.

The cape is store-bought, simple, and helps emphasize the mysterious silhouette of the mask.
The finger claws are fully mechanical: each uses a parallelogram linkage system that extends or retracts the claw as I curl and straighten my fingers, creating a kind of living, mechanical gesture.

Wearing it feels quite straightforward. The claws are pretty identical, so I marked them inside to avoid confusion when putting them on. The mask, however, demands more patience. Since the wiring runs along the same path as the elastic band, the band stretches but the wires don’t, which sometimes causes them to curl up or catch slightly in my hair. The visibility is also limited—mainly because the eye holes are thickened to fit both the LED rings and the acrylic diffusers—so it feels like looking through a narrow tunnel of light.

Through this project, I get to integrate electronics with 3D-printed structures for the first time—how to design around wires, embed boards, and plan assembly digitally before making it physical. I also learned about comfort, weight, and cable management that only appear when you actually wear what you’ve built. If I were to do it again, I would refine the eye geometry to widen the field of view and create more space between the LED ring and the acrylic to enhance the diffusion effect. I would also add more internal padding, especially near the forehead, to keep the wires from pressing awkwardly against the skin.

Material List

  1. 3D pRinter (https://us.store.bambulab.com/products/a1?from=navigation)
  2. PLA filament (https://us.store.bambulab.com/products/pla-matte)
  3. Neo-Pixel Ring *2 (https://www.adafruit.com/product/1643)
  4. Electrical Tape (https://www.amazon.com/Avery-Dennison-Electrical-Commercial-Insulating/dp/B0DQKJV3J4/ref=sr_1_1_sspa?crid=2EQY92WV5OXXZ&dib=eyJ2IjoiMSJ9.lNltt7cumfXsZCkJlMxFYYmx3VDRxwNZBWDZ6mlwLvOONifJA0cbp78rUD3IUGnUBvTgeALCPv1XYlRk49YMiQcDfasuAocTQKEx0ukAZe4KMaFOgrWSC-I5Hq4JO5K8iaXtCNuLT73f79heuIttAIeKtHWw0fzF2GeUK58IKy_nhB-tkTxc7c1VouFUgAj6QsIeQWvo1lfSxh4sSySpATnl2hiqPk1k5m_RY5Fg91ojRj3V-GyE6N9Kb6xyM4gzwqog3yt4mFePmF_iSGklsKl0BiICiTtYsUYw01d_A30.XL2Oq-d_vKzHNwKh_-I81-xds2quiTYmpFHD4Gb_WYk&dib_tag=se&keywords=electrical%2Btape&qid=1762299049&s=industrial&sprefix=electrical%2Btap%2Cindustrial%2C263&sr=1-1-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9hdGY&th=1)
  5. Wires (https://www.amazon.com/TUOFENG-Hookup-Wires-6-Different-Colored/dp/B07TX6BX47/ref=sr_1_1?crid=KVXX8XCZZ5NZ&dib=eyJ2IjoiMSJ9.7QKOdOh0TX6RJzvhBg5Wt1hMobRmxA8YSVMtDCQYCIWI36E-BTkWi1kmCKoq1bvwgGOWhBP7xjQOfb1znb9dQG8fNdqo_7EdUEhqQcJYXGXaPngwFeiIRaaDJqGL22EBsCL5HPtJkmogqW8nxikbR8RWb2fhLNQ1Ed6hr-aQEMO7HF27m281Ci0bJsmk2aptTEuFhorgAikTcjRD_azFEjU_Q0_Pj-SqXF8GHJeWxYEaMiZGTUnc1Fjd2JtemYwE-XwELjimlfLSCtwjFk5NE44CRLG4MdSieIm3UtuSO4I.TIY2x1I6rTAkLZ9KPQinD521UtCnT1EoPmRl5swzVQ8&dib_tag=se&keywords=wire&qid=1762299071&s=industrial&sprefix=wi%2Cindustrial%2C355&sr=1-1&th=1)
  6. EVA Foam 4mm (https://www.amazon.com/Premium-Cosplay-Density-Projects-MEARCOOH/dp/B0BC8RN251/ref=sr_1_1_sspa?crid=OG4F3FRY7IT6&dib=eyJ2IjoiMSJ9.oPwXWY4rs4WfXiN6Et0T3JOS4TOqEbsVcFm2sRnqlYmqM5P59vwxAtHcVN488Osa8uvGeV5EbCFbyara6aSjwubKr3pnUazGggwwY1xf2cLWo2Uymwpj7HV4lIfwh0fLrTBF0TNdTa32R0av8xp_AX2_dvvUmE3kZpkZ4RjcI7se38bam_a-scvIHRL4ZtJSUJ_mfe9-8mNvsRj-DG6mw5x4uMSJ2D2KbgSxcKpQL9Ujy29yiW7jzMqWmgRTnrMKVB8rruPGn5h-4F3N8cUmgWDRETmiJsUgBhw-BaWLw6w.Uqkh7_Iq5tuSXMyyYD7P3O6MwU1GlZdZ–vCyWuuAQ8&dib_tag=se&keywords=eva%2Bfoam&qid=1762298981&s=industrial&sprefix=eva%2Bfoam%2Cindustrial%2C204&sr=1-1-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9hdGY&th=1)
  7. Frosted Acrylic (https://www.amazon.com/dp/B07R9YJ29T?ref=ppx_yo2ov_dt_b_fed_asin_title)
  8. Cape (https://www.amazon.com/dp/B0DSRPDYDZ?ref=ppx_yo2ov_dt_b_fed_asin_title&th=1)
  9. GEMMA M0 (https://www.adafruit.com/product/3501)

Circuit Diagram

Arduino code

#include <Adafruit_NeoPixel.h>

// Pin definitions
#define BUTTON_PIN 0
#define RING1_PIN 1
#define RING2_PIN 2
#define NUM_LEDS 12

Adafruit_NeoPixel ring1 = Adafruit_NeoPixel(NUM_LEDS, RING1_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel ring2 = Adafruit_NeoPixel(NUM_LEDS, RING2_PIN, NEO_GRB + NEO_KHZ800);

int currentMode = 0;
int buttonState = 0;
int lastButtonState = 0;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;

int loadingPos = 0;
unsigned long lastLoadingUpdate = 0;
int loadingDelay = 100;

bool oddLedsOn = true;
unsigned long lastAlternateUpdate = 0;
int alternateDelay = 250;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  
  ring1.begin();
  ring2.begin();
  ring1.show();
  ring2.show();
  ring1.setBrightness(50);
  ring2.setBrightness(50);
}

void loop() {
  handleButton();
  
  if (currentMode == 0) {
    loadingPattern();
  } else {
    alternatingRedPattern();
  }
}

void handleButton() {
  int reading = digitalRead(BUTTON_PIN);
  
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;
      
      if (buttonState == LOW) {
        currentMode = 1 - currentMode;
        
        clearRings();
      }
    }
  }
  
  lastButtonState = reading;
}

void loadingPattern() {
  if (millis() - lastLoadingUpdate > loadingDelay) {
    lastLoadingUpdate = millis();
    
    for (int i = 0; i < NUM_LEDS; i++) {
      if (i == loadingPos || i == (loadingPos + 1) % NUM_LEDS) {
        ring1.setPixelColor(i, ring1.Color(0, 0, 0));
      } else {
        ring1.setPixelColor(i, ring1.Color(255, 255, 255));
      }
    }
    
    for (int i = 0; i < NUM_LEDS; i++) {
      int mirrorPos = (NUM_LEDS - loadingPos) % NUM_LEDS;
      if (i == mirrorPos || i == (mirrorPos + 1) % NUM_LEDS) {
        ring2.setPixelColor(i, ring2.Color(0, 0, 0));
      } else {
        ring2.setPixelColor(i, ring2.Color(255, 255, 255)); 
      }
    }
    
    ring1.show();
    ring2.show();
    
    loadingPos = (loadingPos + 1) % NUM_LEDS;
  }
}

void alternatingRedPattern() {
  if (millis() - lastAlternateUpdate > alternateDelay) {
    lastAlternateUpdate = millis();
    
    oddLedsOn = !oddLedsOn;
    
    for (int i = 0; i < NUM_LEDS; i++) {
      if ((i % 2 == 0 && oddLedsOn) || (i % 2 == 1 && !oddLedsOn)) {
        ring1.setPixelColor(i, ring1.Color(255, 0, 0));
        ring2.setPixelColor(i, ring2.Color(255, 0, 0));
      } else {
        ring1.setPixelColor(i, ring1.Color(0, 0, 0));
        ring2.setPixelColor(i, ring2.Color(0, 0, 0));
      }
    }
    
    ring1.show();
    ring2.show();
  }
}

void clearRings() {
  for (int i = 0; i < NUM_LEDS; i++) {
    ring1.setPixelColor(i, ring1.Color(0, 0, 0));
    ring2.setPixelColor(i, ring2.Color(0, 0, 0));
  }
  ring1.show();
  ring2.show();
}

Discover more from Making Studio

Subscribe now to keep reading and get access to the full archive.

Continue reading