Build Your Own Real Time Frequency Analyzer And Christmas Light Controller!

Editor: In response to a few questions, this SainSmart Solid State Relay Module is the one we used for this project.  The Arduino Nano (linked here) is the breadboardable module you can see; any Arduino will do, however.  We’ve also added a closeup of the board below.


This site is, ostensibly, dedicated to Personal Finance.  Many sites which opine about similar topics are dedicated to shedding off the superfluous tasks and possessions in our lives, but I don’t entirely live by that code.  You see – I live in a neighborhood, and not only do I not feel comfortable letting my front lawn go to prairie but my wife and I like to thrill (annoy?) the neighbors with our excessive spirit come major holidays.

Gaudy?  Ostentatious?  Perhaps.  Worth it?  You be the judge, and if you think so here is all the knowledge you need to put it together.  If you’re here for the PF, either skip this article, or go watch the video.

What Did You Build?

I built a real time frequency analyzer to a microcontroller which doubles as a Christmas light controller.  You can take line in from any source, and depending on the volume in one of 8 audible octaves (the threshold of human hearing is about 10 octaves if you start at 20 Hz), active a relay providing AC power to a plug to light up a strand of Christmas lights representing that frequency.

An original idea?  Of course not – we’ve had music reactive displays for a long time; I just didn’t like the previous implementations I had seen.  By far, the most common way (if you can call it ‘common’) to control Christmas lights is manually timing lights by using software like Vixen or Light O Rama.  That’s fine – I just didn’t want to learn a new piece of software.

Enter this project – 8 plugs (but certainly expandable) controlled by any random song you feel like playing through the line in.

Okay, I’m Sold.  What Do I Need?

  • A microcontroller.  I built it with an Arduino Nano.  Use whatever you’re comfortable with, but for non-5V micros you need to modify some parts.  Let’s call it $20.
  • An 8 channel relay board from eBay – you likely want to get solid state relays since you’ll be switching a lot, and that will kill mechanical relays quickly.  Trust me here.  $15 – $25 depending.
  • A 10k Ohm logarithmic potentiometer (also known as an Audio Potentiometer).  $2.
  • Various hookup wire (note: you should use insulated wire for the signal hookup to the relay board inside the 4-gang box).  $2?
  • Generic 4 Gang electrical box – get a deep one, from any big box store.  I paid like $6.
  • Generic 4 Gang faceplate – isn’t my aluminum nice?.  Another $3.
  • Generic plugs to fit the faceplate – we got ‘designer’ plugs, but you can get rounded. $8.
  • 5-10 feet of Romex for hookup wire.  $10 maybe.
  • Headphone jack (like on your iPod) – get mono, unless you are more ambitious than me.  No idea – maybe 50 cents?  I pulled it off something.
  • 2 100k ohm transistors.  See below.
  • 1 10 uF capacitor.  See below.
  • 1 4.7nF capacitor (for decap – if you have issues with interference, toss a bunch more in there).  All the components, $1 total.
  • Extension cord or power cable to sacrifice.  $5
  • Wire nuts.  $3
  • A promise you know what you’re doing.  The first half you might fry a component, the second yourself – you assume responsibility.  Priceless!
  • Optional – 8 strands of LED Christmas lights (assuming you don’t have them.  We had 4, so I assume you might have some.).  $5 – 8 each.

A number of the Amazon links are tied to our affiliate account; we appreciate your support if you buy through them!

If you’re thrifty, something like $75 – $130.  If you’ve got a bunch of stuff?  You’re getting off cheap.

Building the Input Circuit/Low Voltage Stuff

Here’s my input stage – do NOT hook your headphone jack straight to a uC.  It has alternating voltage on it, and you might get blue smoke instead of a voltage reading.  Here’s how I did it:

Edit: Here’s a closeup of the breadboard from above.

Arduino Nano FFT

(Click to zoom)

The Software

You can use a ton of algorithms here to analyze the input to your Arduino – FFT (Fast Fourier Transform) being the most famous.  I went slightly more obscure with FHT, or Fast Hartley Transform.  Why?  I played with a few libraries, and ended up using the one from Open Music Labs – I highly suggest checking out their benchmarks – FHT is pretty much as fast as you’ll get here, and with an 8 bit microcontroller you need fast (especially when you improve my work).

The code is adapted from the demo code provided by OML, so I am greatly in debt.  My neighbors and I thank you.

Here’s my code (zip, ino file): FHT_XMas

Dry Run

Okay, double check your connections and plug it in.  Load the sketch onto your Arduino (or convert to whatever uc you want to use).  Open a serial monitor at 9600 bps and you’ll start to see numbers scroll by.

Advanced: I have a line in the code, starts with – int oct_bias – you can set those to 0 if you want to see raw data, but you’re aiming for around ‘0’.  In most cases (aka, I tested it on multiple AC plugs and computers in my house), the defaults are fine.  Otherwise, tweak and try to get them near 0.

Plug your Macbook or iPod (Apple doesn’t sponsor this site.) or any audio source into the Arduino – well, any headphone jack.  You should see the numbers jump when you play audio (if you don’t?  First check the audio pot is open.  Turn it.).  You can adjust when the relays are active using the #define THRESHOLD command.  Change it there if the potentiometer isn’t enough for you.

AC Voltage

I really shouldn’t have to say this – but I must.  If you aren’t comfortable working with line voltage do this under the supervision of someone who is.  A random voice on a website won’t cut it – you take responsibility for yourself.  Always double/triple/ and quadruple check for shorts and missed connections before plugging this in.  I’m not showing this step since your circumstances will change based on the relays you choose and the country you visit from.

Okay, it’s game time.  Step one, wire up your relay board.  Most relay boards you buy pre-made are active low.  If yours isn’t FIND/REPLACE the HIGHs and LOWs in my relay code (near the bottom).  My software assumes you will use digital pins 3 through 10 on your microcontroller – so signal pin 3 should go to the relay you want controlled by bass.  Then wire the rest, and provide power and ground form the Arduino.

Hopefully you noted my comment earlier – you should use insulated wire (using some of the Romex would be fine – but you probably want stranded core), rated at at least the peak voltage inside the container.  This isn’t UL listed or anything, but you want to protect what you’ve got in the box.  So divide it if you can, and keep low voltage routes away from high voltage ones.  The relay board should be in the electric box, and the Arduino should be outside!

Test the code again.  For mechanical relays you can hear a click, and for most SSRs you should see a light denoting the channel is on.  If so, unplug the Arduino and it’s time to wire for higher power!

Break off the tabs on the ‘hot’ side of your plugs (line).  (In the US, that’s the side WITHOUT ground).  Hook up your grounds to the extension cord (run it into the box), keep it tight – make sure to test fit it in the box.  Hook up the commons (load) (the white wire in many jurisdictions, check local codes/ask a friend if you don’t know), using pieces of ROMEX and wire nuts. (A note: don’t be snarky – I know you could do the same with load.  However, you want that wire to go directly to the relay – if you wire hot directly to the plugs, there will still be a charge on the plugs when the relay is switched off!)

Now, hook up hot!  This will change based on the relay, but you basically want 8 channels wired together INTO the relay, and a single wire FROM the other side of the relay to each of the 8 line terminals (Again, that’s the isolated side on your plugs).

You now need to push it back together.  Just like replacing a switch in a doubly tapped circuit, check if any wire is exposed (your call on how to re insulate it – electrical tape comes in insulation rating as well; note it).  Carefully slide them down and screw them in.  Put the plate on top.  Your ground is working, right?  Check again, especially if you have a metal faceplate.

Power Delivery – Why You Should Use LED Strands

Most pre-made solid state relay boards won’t switch more than 2 amps, and even most boards of solid state relays switch just 10.  However, your practical limit when you have 8 outlets butts up against total branch power.

For most rooms, you’ve got 15 amps total to play with – a total of only roughly 1700 watts.  Subtract power for anything else you have running on that circuit – computers and a stereo, perhaps?  Additionally, you’re switching this power through a pretty shallow box – you need to consider the heat build-up.

What to do?  Grab some 2 or 4 watt LED Christmas light chains.  Even if you put 2 strands on each plug, worst case you’re only talking 64 watts total – about the same as some single incandescent bulbs!  You’ve also spread the heat across the 8 relays instead of pushing 64 watts through a single one.  Plus, they look the same.  If you haven’t switched to LEDs yet, here’s your chance.

(Note that common 220 watt incandescent strands mean 1760 watts with just a single strand per plug, which might be enough to trip your breaker at a particularly loud section of music.  DON’T TRY IT.)

Oh yeah, Personal Finance note (for the regulars!): it’s much cheaper to run LEDs.  One strand needs to run for over 2 days to match the power used by an old strand in an hour.  Crazy.


If you’ve quadruple tested, and doubted everything I said and checked elsewhere on the internet (remember, it’s on you!  I majored in EE/CS, but I code all day.  I’m way more theory than practice), it’s go time.  Plug in your Arduino and 8 LED strands and bulbs.

Turn on the music.

Plug it into a GFCI outlet (which should mean anything in your kitchen or outside.  Ask your friend if you don’t know.), maybe with a surge protector (oh, and fire extinguisher nearby!).

Let there be light! (And if not, UNPLUG it before debugging.).
Here’s a shot of mine working with the real time sounds of Beethoven’s 9th Symphony.  Yes, it’s a 7-bit FHT in the video – one of the relays on my board was broken.  It’s another reason to get Solid State Relays to switch those LEDs – I’ll upload another video when I install the SSRs.

(I know, the audio sounds horrible. Blame my cell phone.  If you want to listen to me with some better audio, check out my podcast.)

Like it?  Hate it?  Want to make fun of something?  Improved it?  Let me know, by email or through comments.


    • says

      I’ll edit it into the article – I think if you had a lot of supplies or stopped at the low voltage phase you could do it. I had a lot of parts on hand.

      Let me know if you take it on!

  1. AverageJoe says

    This is just electrician porn, isn’t it? Isn’t it? I know…everyone just comes here for “the articles.” Whatever.

  2. Tom Milburn says

    The left two numbers do not react at all (x1 and x2) Everything else is rocking like it should.

    • PK says

      That’s great!

      One thing to check – assuming your microphone is picking up the lower frequencies, have the Arduino spit out the levels over Serial. If those numbers are at a permanent plateau (or in a valley), adjust the biases significantly and maybe you’ll be able to get a reaction.

      I’m about the pull this project out of the closet again for Halloween/Christmas.

  3. subnet says

    great article thanks for sharing! i’m happily awaiting parts to arrive.. in the meantime, i don’t see the video that you reference above that shows it in action – could you paste a link? thanks in advance

  4. subnet says

    well i’ve got it all hooked up using some test leds i picked up for the outputs and it seems to be working good – when there’s signal that is.

    i seem to be having a problem when there’s no signal – about 1/2 the pins show high (i’m modifying this so that each pin is high when the respective frequency range that it’s assigned to is at a higher number = 100+ value will turn led HIGH). i’m running it at FHT_N 32 so that i can capture more of the frequency range in each pin rather than 256 as i would only really be touching the lower (and narrower) register of frequencies if i was to use 256 (right?). i’m going to do more with this ranging aspect once i get this no signal issue fixed but for now it seems appropriate to stick with the wider range of frequencies per pin since i’m only using the 1st 8 of them.

    here’s some content of the 32 bins when silent and no bias applied to get raw numbers (the numbers stay fairly consistent, the brackets signify the ones i’m using to trigger):

    [88 31 22 29 192 255 27 0] 253 255 254 253 -3 -4 250 249 247 247 246 245 -11 -12 242 256 255 256 1 -12 -104 -88 224 233

    here’s some content of the 32 bins with audio and no bias applied to get raw numbers (the numbers fluctuate as expected, the brackets signify the ones i’m using to trigger):

    [184 127 81 86 186 241 177 8] 17 255 223 253 230 251 234 249 1 -8 236 245 7 -12 218 256 9 1 6 -12 49 70 150 175

    so as you can see throughout the range (if you look beyond the first 8), quite often while silent the base numbers that you would expect to be lower, are actually much higher than expected – and if i was to adjust this with the bias, then the leds wouldn’t trigger properly since the lower limit of the bias would need to be set too high – and when there’s audio the numbers wouldn’t fluctuate as expected as the bias would be restricting an led from being triggered when it should be (e.g. in the case above see indexes 4 and 5.

    any ideas on what could be the problem, or how to overcome this?

    • PK says

      Do you see any movement at all in the frequency range? When I played with the 8 bins, even the octaves that were strangely high I could just use a larger bias or prescale so that it would work kind of normal. What happens when you apply those ‘silent’ numbers to bias, then adjust the threshold down for the bins which have the largest biases set? At some point, note that it could be an equipment issue – you need more/less gain (put an amplifier in front of it – I have a ton of LM386s, so I experimented with that), or a higher quality mic/multiple mics. In that case best you can do is probably restrict it to the frequency ranges you’re seeing perform best.

      One idea that works if things are too volatile (don’t see if you have that problem) is a rolling average too – just make sure it doesn’t roll long enough for your eye to think the lights have latency.

  5. subnet says

    hello, yep there’s movement in the frequency ranges when the music is playing – that’s why i’m hesitant to set the silent bias per channel so high as it will stop the frequency from reacting as it should.

    i’m using line out from the laptop right now for testing and the potentiometer seems to do its thing correctly so will be useful in minor adjustments but in real life conditions it’ll be connected to a mixer feed as its going to be a lighting array for the band.

    i thought about checking for a range of values that i would know things are silent but had hoped to solve this in a better way so that the numbers are more representative of their actual state – i don’t even know where to start if its a hardware issue. if there’s anything else you can think of that i can test or do please let me know. thanks again for sharing your schematic and knowledge it was very helpful in getting this all going. :)

  6. Chris Cowan says

    So excited to make this happen this year. Got my Ardurio Nano and went to up load the code on to it and this is what I got…

    This report would have more information with
    “Show verbose output during compilation”
    enabled in File > Preferences.
    Arduino: 1.0.6 (Windows NT (unknown)), Board: “Arduino Uno”
    FHT_XMas.ino: In function ‘void loop()’:
    FHT_XMas:99: error: ‘fht_input’ was not declared in this scope
    FHT_XMas:101: error: ‘fht_window’ was not declared in this scope
    FHT_XMas:102: error: ‘fht_reorder’ was not declared in this scope
    FHT_XMas:103: error: ‘fht_run’ was not declared in this scope
    FHT_XMas:104: error: ‘fht_mag_octave’ was not declared in this scope
    FHT_XMas:108: error: ‘fht_oct_out’ was not declared in this scope
    FHT_XMas:113: error: ‘fht_oct_out’ was not declared in this scope

    Any ideas

  7. Matthew N says

    If you want a line out so you can play the music you are analyzing I used use a stereo hookup instead of mono and used the right channel to analyze and left for line out. This gives a mono output for sound but to prevent only getting one channel I merged the L and R channels I then output the combined L and R on BOTH channels so to analyze one and one for line out.