Project tutorial
Monster in a Box

Monster in a Box © GPL3+

A Monster in a Box Halloween prop is a box or crate that appears to be jailing a monster which is on the verge of escaping.

  • 127 respects

Components and supplies

About this project

*** UPDATED to Ver 4 ***

I recently rewrote the Arduino code for this project. Here is a summary of the changes:

  • Main loop is now implemented as a state machine
  • All timing now based on event time vs. current time evaluations

- timing is no longer dependent on frames

- allowed removal of delay() in the main loop

- eliminates the 'port busy' issue when connecting to Arduino IDE

  • Relays now using Normally Open rather than Normally Closed connections *** Update your Relay Connections!!! ***
  • Added Motion Detected LED
  • Added Ready to Wake LED
  • Added Paused LED
  • Removed Action Trigger Switch
  • Added ability to add extra time to Red Lights & Smoke

- allows these effects to run a bit longer than the lid bouncer

  • Added lots of comments

You will find all the latest code here on github

Monster In A Box

I love Halloween props and have seen and read about some great Monster in a Box builds this past year and decided that I should build my own. The idea is pretty simple: create the illusion that some type of container is holding a dangerous monster that is on the verge of escaping. The illusion can be created with any combination of motion, sound, light, smoke and, of course, surprise.

Finally got a good video of this in the 'wild' this year :)

What's in the Box!?!?

You want a box that is large enough that it appears to be capable of holding something big and scary. It also adds to the illusion if the box appears to be old, well worn, and just maybe only strong enough to keep the monster at bay. At first I shopped around at thrift stores and online for something but never something that I liked, so I build my own. It was not very difficult.

I used 15 6ft pine fence pickets from Home Depot to make a box that measured about 3ft x 2.5ft x 2.5ft. I cut all the board to length with a table saw and assembled the box using a pneumatic 18 gauge brad nailer. This appeared to work well, but later I realized that the brads were not strong enough to hold the box together due to all of the banging and bouncing it would be doing. To strengthen the box I later screwed it together with 1 inch dry wall screws (from the inside out so they would not show).

Once built I aged the wood with a process that used black tea, vinegar and steel wool. I then decorated it with stencils and labels that I found online and/or created and printed.


The prop has 2 operational states "Sleeping" and "Awake". While sleeping the box is relatively calm and only makes snoring and light growling sounds. The monster is awoken when a trick-or-treater trips a motion sensor. When the monster is awoken the monster roars, breaths smoke, bounces the lid, and the inside of the box glows red.

I used an Arduino Uno along with a WaveShield and a Relay Module to orchestrate the behavior of this prop. The WaveShield is loaded with a small collection of sleeping and roaring sounds. A PIR motion sensor provided input to the Arduino to switch the box between "sleeping" and "awake" modes. During awake mode the Arduino activates relay channels to run the lid motor, red lights, the smoke generator. and play the sounds on the WaveShield. One other feature that I added was a Pause Button. This allowed me to temporarily silence the monster when younger kids come by.

It's Aliiiiiive!

A lid lifter is a device that will quickly raise and lower the lid to make it bang and bounce as if the monster is trying to escape. There are several ways this can be done the most popular appear to be pneumatic actuators and rotating cams. Although I think an electronically controlled pneumatic actuator would have allowed me to make the lid motions much more random, I decided to go with the a rotating cam as it seemed to be the simpler and least expensive option.

I bough a used automotive windshield wiper motor from a local scrap yard for $15. It was kinda funny. I walked in and asked for a motor. The guy behind the counter asked "for what make and model car" when I said "I dont care" his head tilted to the side a bit and he gave me a bit of side eye. After I explained what I was up to he wanted to build one :)

These motors, regardless of make/model, run on 12 volts, spin at a reasonable rate and have great torque: perfect! Note. When you get one make sure it has some of the old wire harness it will make it much easier for you to splice your wire to it. also make sure it has its crank arm and offset post. If the main motor post is the only physical connection you have to work with your cam will likely slip.

Mount the motor somewhere inside your box. I put mine on one of the side walls. Remember there will be a large rotating cam connected to it and it needs to be able to rotate free and clear of any and all items, especially wires, that you are going to put in the box. There are several good tutorials on YouTube on working with these motors.

I cut a 12 inch diameter cam from a piece of 1/2 ply wood. first I cut a perfect circle. I mounted it on the motor and used this to determine the required height of the motor in the box. I only wanted the lid to lift about 2 inches. After doing this fitting I used a jig saw to randomly cut 'teeth' into the cam trying my best to keep them at random width and depths.

Lastly I mounted a roller to the under side of the lid using 2 metal L brackets, a 1 inch nylon spacer and a carriage bolt. The roller is positioned just above the cam so that it rides along the teeth of the cam.

I used the 12 volt rail of an ATX power supply to power the motor through the relay controlled by the Arduino. Yes, getting everything to line up does require a bit of trial and error. After re-positioning the roller once or twice... ok three times... everything lined up and the lid began bouncing as planned!

Breath of Fire : Smoke and Lights

I used a cheap 400 Watt smoke generator for smoke effects. Out of the box this was radio controlled. To control it with the Arduino I opened the receiver and soldered two lead lines to the trigger button pads. This provided me with lines that I connected to an Arduino controlled relay channel. It was a simple hack that I learn by watching a few YouTube videos. I also added an old vacuum cleaner hose to direct the smoke to come out as a stream from just under the lid. I liked the visual effect and it help reduce the vapor condensation on the electronics inside the box.

For lights I simple cut a light duty extension cord and wired it through a relay channel. The string of 100 red LED lights connected to this could then be switched on and off by the Arduino.

Use Your Outside Voice

The WaveShield has an 1/8 inch headphone jack as and output - as well as 2 i/o pins for a direct connection. I used an 1/8 inch patch chord to connect to a 30 Watt bass guitar amplifier that I borrowed from my practice room. Any number of amplified speakers would work - including PC speakers. But using something with solid low end is definitely a plus.

Chain It Up

I purchase some plastic chains from a Halloween store and 3D printed a pad lock. Wrapping these over the crate added to the depth and feel of the illusion.


Put the crate out in the yard and hide the motion sensor in a nearby bush. I used a long run of speaker wire to connect the sensor so that I could experiment with the positioning such that the monster would wake up when someone gets close (about 5 ft) to the crate.

As the evening grew darker I realized that all of the detail on the outside of the crate was becoming difficult to see. Eventually I illuminated it with a strobed floodlight and was very happy with the effect.

As trick-or-treaters walked up the driveway they notice the growling box: some with curiosity others were actually scared of it. As they approached and the monster roared to life they would up, and squeal some even applauded. It was a huge hit.


I got tired of looking for ways to hide my Motion Sensor in the bushes so I hot glued one into the belly of a plastic rat that I can position somewhere along the driveway or under that nearby bush. I also installed an RJ11 phone jack on the rear of the box so that I could connect the rat motion sensor using a 50ft phone chord - which is much better than the two strands of speaker wire that I was previously using.

You will note that I also added a switch and some indicator LEDs to that new plate. The switch PAUSES all functions on the box without powering it down. My main kill switch is the power strip inside and is cumbersome to access, plus with this only uses signal voltage (5v) on this external switch. This switch has been great for stopping the monster for younger trick-or-treaters and others that ask for a peek inside on Halloween night.

Oh! I drape plastic chains over box for effect and they can be unwieldy at times. Especially if I need to get into the box during the evening. To make this easier I zip tied black spring carabiners to the ends of the chains. This lets me quickly un/hook the ends to eyelets that I screwed into the lower corners of the box. Not very high tech but very helpful.

The Future

There are a few things that I want to add in the future. Maybe R/C control so that I can pause or trigger the monster from a distance. I will also add some type of feedback to the lid lifter so that the Arduino can 'know' when the lid is open or closed. Several times the lifter stopped at a high point in the cycle which made the 'guts' of the box visible until the next wake cycle. Lastly, I may connect the external lighting/strobes to the Arduino to allow them to be controlled by the program and or the R/C fob.

GitHub Repository

You will find the latest code and wiring diagrams Here on GitHub


Monster In A Box SketchC/C++
Wave Shield Pins in Use: 2, 3, 4, 5, 10, 11, 12 & 13
Pins 13, 12, 11 are always used by the SD card (they are the only pins that have a high speed SPI interface). 
Then there are 5 other pins used to talk to the DAC and SD card, but they can be set to connect to any arduino pin. 
However, by default, the library is configured to use pins 10 (for SD card) and pins 2, 3, 4 and 5 for the DAC. 
To chanage these pins requires modifying the library - the pins are referenced by their 'hardware' pin names (ie PORTD, etc) 
not by arduino pins. 

That means pins 6, 7, 8, 9 and the 6 analog in pins (also known as digital i/o pins 14-20) are available.
#include "MonsterSounds.h"

#define RESERVED_00   0   // Reserved for Serial RX
#define RESERVED_01   1   // Reserved for Serial TX
#define RESERVED_02   2   // Reserved for Wave Shield
#define RESERVED_03   3   // Reserved for Wave Shield
#define RESERVED_04   4   // Reserved for Wave Shield
#define RESERVED_05   5   // Reserved for Wave Shield

#define FOG_MACHINE   6   // Connect Digital Pin on Arduino to Relay Module
#define RED_LEDS      7   // Connect Digital Pin on Arduino to Relay Module
#define LID_BOUNCER   8   // Connect Digital Pin on Arduino to Relay Module
#define RESERVED_09   9   // Connect Digital Pin on Arduino to Relay Module

#define RESERVED_10  10   // Reserved for Wave Shield
#define RESERVED_11  11   // Reserved for Wave Shield
#define RESERVED_12  12   // Reserved for Wave Shield
#define RESERVED_13  13   // Reserved for Wave Shield

#define PIR_SENSOR      A0 // PIR Input
#define MOTION_LED      A1 // LED: lights when motion is detected (regardless of pause/sleep/wake state)
#define PAUSED_LED      A2 // LED: lights when system is paused
#define READY_LED       A3 // LED: lights when monster is in READY_TO_WAKE state
#define PAUSE_BUTTON    A4 // Pause Switch Input
#define DEBUG_BUTTON    A5 // Debug Switch Input

// Effects Timer Settings - in Seconds (EDIT THESE)
#define WAKE_DELAY           30 // Minimum amount of time between 'awake' occurencs in Seconds
#define WAKE_DELAY_DEBUG     10 // WAKE_DELAY override when DEGUB switch is engaged
#define SLEEP_SOUND_DELAY     1 // Number of seconds to wait between attempting to fire the next 'sleep' sound
#define WAKE_MIN              3 // Minimum amount of 'awake' time in Seconds
#define WAKE_MAX              5 // Maximum amount of 'awake' time in Seconds
#define RED_LIGHT_EXTRA_TIME  1 // Allows the red lights to run a bit longer than the lid bouncer, if desired
#define SMOKE_EXTRA_TIME      2 // Allows the smoke to run a bit longer than the lid bouncer, if desired 

// Effects Timers Settings - in Milliseconds (DO NOT EDIT THESE)
#define WAKE_DELAY_MILLIS             WAKE_DELAY * 1000
#define WAKE_MIN_MILLIS               WAKE_MIN * 1000
#define WAKE_MAX_MILLIS               WAKE_MAX * 1000

MonsterSounds sounds;

static unsigned long timeSinceLastSnore= 0;
static unsigned long wakeAllowedTimer = 0;

static unsigned long lidBounceTimer = 0;
static unsigned long lidBounceDuration = 0;

static unsigned long smokeTimer = 0;
static unsigned long smokeDuration = 0;

static unsigned long redLightTimer = 0;
static unsigned long redLightDuration = 0;

enum States {
   STATE_INITIALIZE,     // Only while running setup() and first time into loop()
   STATE_PAUSED,         // Turn off all sounds and effects
   STATE_SLEEPING,       // No effects, sleeping sounds, does not allow awake to be triggered
   STATE_READY_TO_WAKE,  // No effects, sleeping sounds, allows awake to be triggered
   STATE_AWAKE};         // Fires effects and monster awake sounds

States state = STATE_INITIALIZE;

void setup() 
  // initialize serial communication:
  // Setup all the relay Pins
  pinMode(RED_LEDS,    OUTPUT);
  pinMode(READY_LED,   OUTPUT);  

  // Force all Effects to OFF

  pinMode(PIR_SENSOR,      INPUT);

  sounds.initialize(); // Monster Sounds
  sounds.playSystemReady(); delay(1000);
  Serial.print("*** System Ready ***");

 * NOTE: All buttons are using pullups, thus LOW means the button is PRESSED
 *       Keep in mind the pull-up means that swiitch logic is inverted. 
 *       It goes HIGH when it's open, and LOW when it's pressed. 
 *       The PIR motion sensor does NOT behave this way.

 * Main processing loop
 *     - Manages the Monster's State Machine
void loop() {    

  boolean pauseSwitchClosed = digitalRead(PAUSE_BUTTON) == LOW;
  boolean motionDetected    = digitalRead(PIR_SENSOR) == HIGH;
  digitalWrite(MOTION_LED, digitalRead(PIR_SENSOR));
  switch (state) {
      if (pauseSwitchClosed) {  goToPause(); } 
      else { goToSleep();  }
    case STATE_PAUSED:
      if (!pauseSwitchClosed) { goToSleep(); digitalWrite(PAUSED_LED, LOW); }
      else { digitalWrite(PAUSED_LED, HIGH); }
      if (pauseSwitchClosed) { goToPause(); } 
      else if ( isAllowedToWake() ) { goToReadyToWake(); } 
      else { processSleeping(); }
      if (pauseSwitchClosed)   { goToPause(); digitalWrite(READY_LED, LOW); }   
      else if (motionDetected) { goToAwake(); digitalWrite(READY_LED, LOW); }
      else { processSleeping(); }          
    case STATE_AWAKE:  
      if (pauseSwitchClosed){ goToPause(); }      
      else if ( processAwakeAnimation() ) { goToSleep(); } // processAwakeAnimation() returns true when all animations are complete
    default: Serial.println("UNKNOWN STATE"); break;  // We should never get here      

 * Transition to the Pause State
inline void goToPause() {
  state = STATE_PAUSED;

 * Transition to the Sleep State
inline void goToSleep() {
  Serial.println("GOING TO SLEEP");
  wakeAllowedTimer = millis();

 * Transition to the Ready To Awake State
 *     This is a special case of the sleeping state
inline void goToReadyToWake() {
  Serial.println("READY TO WAKE");
  state = STATE_READY_TO_WAKE;  

 * Transition to the Awake State
 *     - Wake the monster and process the effects & sounds
inline void goToAwake() {
  state = STATE_AWAKE;

 * process a cycle of the SLEEP activity
 *    - Run Sleep Sounds
 *    - Update sleep timer
inline void processSleeping() {
  if ((millis() - timeSinceLastSnore) > SLEEP_SOUND_DELAY_MILLIS) {   
      timeSinceLastSnore = millis();        

 * Determines if monster is allowed to wake up at this time.
 *      - Check debug switch, if its closed we use a shorter Wake Allowed Timer
 *      - Monster must sleep for a predefined minimum amout of time before it may be awoken
 *      - Illuminate LED when it is ready to be awoken
inline boolean isAllowedToWake() {
  boolean isDebug = digitalRead(DEBUG_BUTTON) == LOW;
  unsigned long requiredDelay = WAKE_DELAY_MILLIS;
  if ( isDebug ) { requiredDelay = WAKE_DELAY_DEBUG_MILLIS; }
  boolean isAllowed = (millis() - wakeAllowedTimer ) > requiredDelay;

  if (isAllowed ) { digitalWrite(READY_LED, HIGH); } 
  return isAllowed;

 * Wake Monster
 *   Starts the Awake Animations
 *   Plays the Awake Sounds
 *   Call this ONCE to start the AWAKE state.
void wakeMonster() 
  int activityDuration = random(WAKE_MIN_MILLIS, WAKE_MAX_MILLIS); // this is how long the monster will be active

  Serial.print("   wake duration: ");
  Serial.println(" ms ");
  flashRedLight(activityDuration + RED_LIGHT_EXTRA_TIME_MILLIS);
  activateSmoke(activityDuration + SMOKE_EXTRA_TIME_MILLIS);    

 * Manages the progress of the AWAKE animations
 *    Call this EVERY CYCLE during the AWAKE state.
 *    Returns TRUE when all animations are complete
boolean processAwakeAnimation()
  boolean done1 = false;
  boolean done2 = false;
  boolean done3 = false;
  if (millis() - lidBounceTimer > lidBounceDuration) 
    done1 = true;
  if (millis() - redLightTimer > redLightDuration ) 
    done2 = true;
  if (millis() - smokeTimer > smokeDuration )
    done3 = true;
  return done1 && done2 && done3;

 * Manage Effect: Bounce the box lid
 *     - diration is the number of milliseconds that the effect should run
 *     - duration of 0 means the effect should be stopped
inline void bounceLid(unsigned long duration)
  if (duration <= 0) 
    lidBounceDuration = 0;
  } else {
    // start the lid bouncing
    lidBounceTimer = millis();
    lidBounceDuration = duration;

 * Manage Effect: Flash Red Lights
 *     - diration is the number of milliseconds that the effect should run
 *     - duration of 0 means the effect should be stopped
inline void flashRedLight(unsigned long duration)
  if (duration <= 0) 
    redLightDuration = 0;
  } else {
    // start the light flashing
    redLightTimer = millis();
    redLightDuration = duration;

 * Start/Stop Effect: Activate Smoke
 *     - diration is the number of milliseconds that the effect should run
 *     - duration of 0 means the effect should be stopped
 inline void activateSmoke(unsigned long duration)
  // 'press' the smoke button
  // duration should be a fixed amount of time needed for the machine to respond to the action
  // set a timeout to stop after duration
  if (duration <= 0) 
    smokeDuration = 0;
  } else {
    // start the light flashing
    smokeTimer = millis();
    smokeDuration = duration;

 * Stop all of the Effects 
 *         - This effectively turns the monster off
inline void stopAllEffects()

 * Prints the Awake Animation timers to the log once per second
inline void printTimersToLog() {
  static unsigned long timeofLastTimerLog = 0;
  if (millis() - timeofLastTimerLog >= 1000)  {
    Serial.print("   lid: ");
    Serial.print( (millis()-lidBounceTimer) > lidBounceDuration ? 0 : (lidBounceDuration -(millis()-lidBounceTimer) ) );
    Serial.print("  lights: ");
    Serial.print( (millis()-redLightTimer) > redLightDuration ? 0 : (redLightDuration -(millis()-redLightTimer) ) );
    Serial.print("  smoke: ");
    Serial.println( (millis()-smokeTimer) > smokeDuration ? 0 : (smokeDuration -(millis()-smokeTimer) ) );
    timeofLastTimerLog = millis();

 *  Energize Relay
 *  Sets the Normally Open (NO) terminal to OPEN
 *  Normally Closed will become Closed
 inline void energizeRelay(int channel)
  digitalWrite(channel, HIGH);

 *  De-Energize Relay
 *  Sets the Normally Open (NO) terminal to CLOSED.
 *  Normally Closed will become OPEN
 inline void de_energizeRelay(int channel)
  digitalWrite(channel, LOW);
Arduino header for the monster sounds library
 * Pins 13, 12, 11 are always used by the SD card (they are the only pins that have a high speed SPI interface). 
 * Then there are 5 other pins used to talk to the DAC and SD card, but they can be set to connect to any arduino pin. 
 * However, by default, the library is configured to use pins 10 (for SD card) and pins 2, 3, 4 and 5 for the DAC. 
 * To chanage these pins requires modifying the library - the pins are referenced by their 'hardware' pin names (ie PORTD, etc) not by arduino pins. 
 * That means pins 6, 7, 8, 9 and the 6 analog in pins (also known as digital i/o pins 14-20) are available.
#include <Arduino.h>
#include <Wire.h>
#include <WaveHC.h>
#include <WaveUtil.h>

    static const char roar0[] = "ROAR0000.wav";
    static const char roar1[] = "ROAR0001.wav";
    static const char roar2[] = "ROAR0002.wav";
    static const char * const roarSounds[] = {roar0, roar1, roar2};
    static const char sleep0[] = "SNORE000.wav";
    static const char sleep1[] = "SNORE001.wav";
    static const char sleep2[] = "SNORE002.wav";
    static const char * const sleepSounds[] = {sleep0, sleep1, sleep2};
    int previousRoarSound = -1;
class MonsterSounds 

    SdReader  card; // This object holds the information for the card
    FatVolume vol;  // This holds the information for the partition on the card
    FatReader root; // This holds the information for the volumes root directory
    FatReader file; // This object represent the WAV file for a phrase
    WaveHC    wave; // A single wave object -- only one sound is played at a time
    void playfile(char *name);

    void initialize(); 
    void playSystemReady();
    void playRoar();    
    void playSnore();    
    void stopAll();
Arduino code for the monster sounds library
 * Pins 13, 12, 11 are always used by the SD card (they are the only pins that have a high speed SPI interface). 
 * Then there are 5 other pins used to talk to the DAC and SD card, but they can be set to connect to any arduino pin. 
 * However, by default, the library is configured to use pins 10 (for SD card) and pins 2, 3, 4 and 5 for the DAC. 
 * To chanage these pins requires modifying the library - the pins are referenced by their 'hardware' pin names (ie PORTD, etc) not by arduino pins. 
 * That means pins 6, 7, 8, 9 and the 6 analog in pins (also known as digital i/o pins 14-20) are available.
void MonsterSounds::initialize()
  Serial.println("Initializing Sounds...");
  if(!card.init())        Serial.println(F("Card init. failed!"));
  if(!vol.init(card))     Serial.println(F("No partition!"));
  if(!root.openRoot(vol)) Serial.println(F("Couldn't open dir"));
  Serial.println(F("Files found:"));;


void  MonsterSounds::playSystemReady()

void  MonsterSounds::playRoar()
  int index = random(3);  // 0, 1, 2
  while (index == previousRoarSound)
    index = random(3);
  previousRoarSound = index;

void  MonsterSounds::playSnore()
  if (!wave.isplaying) // Do not interupt an exising sound with snoring
    int index = random(3); // 0, 1, 2

void MonsterSounds::stopAll()
  wave.stop(); // Stop any currently-playing WAV

// -------------------------------------------------------------------
// playfile()    
// Open and start playing a WAV file
// -------------------------------------------------------------------
void MonsterSounds::playfile(char *name) 
  PgmPrint("Playing sound: ");
  if (wave.isplaying) {// already playing something, so stop it!
    wave.stop(); // stop it

  if (!, name)) {
    PgmPrintln("File not found ");

  if (!wave.create(file)) {
    PgmPrintln("Not a valid WAV");
  // ok time to play!;

Custom parts and enclosures

Monster In A Box Sounds Zip


Monster In A Box
Monster in a box diagram upctxtmu0r


Similar projects you might like

You Can't Creep up on This Creepy Cauldron!

Project tutorial by Barton Listick

  • 50 respects

Spooky Candy Box

Project tutorial by Shoeb Ahmed

  • 5 respects

Mini Me 1.5 - Adafruit HUZZAH Robot Doll

Project tutorial by Kitty Yeung

  • 32 respects

Optical Illusion Box

Project tutorial by Patt Vira

  • 24 respects

The Badland Brawler - RC Monster Truck with Arduino

Project tutorial by Jithin Sanal

  • 128 respects
Add projectSign up / Login