🎥
Skip to the end for a video demo

Halloween is a great holiday because it's all about creativity. As a younger kid I had a lot of fun carving pumpkins and crafting the perfect homemade costumes to show off to my friends. Eventually I outgrew trick or treating, and the holiday lost some of its magic. This year was special though. My life as a student came to a close in May, and for the first time I'm living like a real adult independently my own home. My house is situated in a suburban neighborhood with good sidewalks and plenty of families, which means I'm in trick-or-treater territory. This time, I'm the one giving the candy!

I knew I had to do something to spruce up my porch, but I also didn't want to sink hundreds of dollars into tacky decorations that would only be up for a couple weeks. When I spotted one of my neighbors donning a creepy green bulb in their porch light fixture, I realized I could make a cool audio/visual light show on my porch without breaking the bank.

First I'd need some smart lights. I read good things about the TP-Link Kasa line of smart lights as a cheaper alternative to the Hue ecosystem. I ordered the 2-pack of KL125 bulbs off Amazon. They were easy to set up using the Kasa Smart app on my phone, and more responsive than I'd expected (it felt nearly identical to the Hue products I've used in the past). Since I had 2 bulbs, I put one outside on my porch, and another one inside pointed at the window blinds (which diffuses the light and makes the whole pane glow).

While there isn't a public API for the Kasa bulbs (one downside compared to Hue products), there is a TP-Link Kasa Home Assistant integration. I figured I'd installing Home Assistant on one of my old Raspberry Pis, but to my dismay the official RPi images for Home Assistant only supports Raspberry Pi 3 and newer. I was scraping this all together on Halloween morning, so I didn't have time to waste on workarounds for my older Pi 2B+.

Luckily, I didn't have to install Home Assistant all. A kind soul named Patrick Seal published a Node.js library tplink-smarthome-api for controlling TP-Link Kasa devices over the local network. Within a few minutes I could change the brightness and hue of my lights from a script on my laptop. It comes with a few CLI commands too, notably:

  • tplink-smarthome-api search to determine the IP addresses of my bulbs
  • tplink-smarthome-api getInfo <IP>   to determine the hue/brighness of a bulb after I'd adjusted it via the Kasa app

Here's a small example of how the API looks for changing a light's state:

const { Client, Bulb } = require("tplink-smarthome-api")

const client = new Client()
const bulbPorch = await client.getDevice({ host: HOST_BULB_A })

// Set bulb to max brightness with red hue over 200 ms
await bulbPorch.lighting.setLightState({
	on_off: 1,
	hue: 0,
	saturation: 100,
	color_temp: 0,
	brightness: 100,
	transition_duration: 200,
})

I needed a way to play audio too. For this I used an old Echo Dot which doubles as a bluetooth speaker. It's small enough to fit widged between the glass and screen in the window facing my porch, so kids can hear it outside (mic muted) while I control it inside, and I don't have to worry about it getting stolen or damaged. I'd drive it with a web app running on my laptop inside the house. I used Tone.js to mix audio on the frontend.

Now I just had to design the experience. I'm currently playing through the Zelda games on N64, so one sound I knew I had to include was the Potion Shop music from Ocarina of Time. I also picked the creepy laugh of the Happy Mask salesman from Majora's Mask. Lastly I found some thunder and horror transition sounds online.

The flow would be:

  • Kid rings the doorbell
  • Porch light flickers and creepy laugh is heard
  • Loud scary noise, light turns red
  • Thunder noises and lightning flashes from the window
  • I open the door, both lights fade across eerie colors and Potion Shop music plays as kids take their candy

I used an Express server handle lighting commands on the backend, and made buttons to orchestrate that with the audio in a simple HTML/JS front end. I just had these 4 buttons to trigger each animation state for the lights, with numbered keybinding added for convenience (e.g. press "1" to reset)

Lastly, I got a $10 gas mask from CVS and donned my finest black loungewear to complete the eerie vibe.

Matt stands inside wearing all black clothes, black gloves, and a black plastic gas mask. He holds a black tray filled with candy. Next to him stands a tall light fixture with his smart bulbs casting blue and green light over him.

From the kids' point of view, this is what it'd be like after knocking on the door or ringing the doorbell (sound on).

The kids took it surprisingly well. I think the older kids actually got more scared than the little ones. Lots of kids complimented my costume and the overall setup. I tried to compliment them all back, but I was honestly so caught up in scrambling to put on my gloves & mask each time the doorbell rung, all while pressing buttons on my laptop in quick succession, that I barely could pay much attention to all their costumes. I'm just glad I didn't run out of candy (I started with at least 100 pieces and had 12 left at the end of the night).

Some things I'd do differently next time:

  • Have one button to queue the whole script, instead of buttons for each effect
  • Open the door sooner in my script (some kids aren't that patient and started to leave thinking nobody's home)
  • Make it trigger from my phone so I don't have to run across the house
  • Have some way to see what's happening outside (camera? peephole?)
  • Make it scarier! Kids thought it was cool, but no one screamed.

Looking forward to stepping up my scare game next year.