This project was done almost entirely within the realm of software, using the Intrinsic CerfBoard embedded linux solution. The light strands are controlled via the output of the 16 GPIO pins on the CerfBoard -- a logical "high" means the light strand is on, and a logical "low" means that the light strand is off. Although for our prototype, we used LED's, there is no reason why you can't hook up full-blown strands of Christmas lights using transistors and/or relays.
The design was broken into two main parts: a linux kernel module for handling the timing-critical light switching, and a translator that takes in the LISHP protocol and outputs a control string to the kernel module. This translator can be listening on a serial port, and process any incoming commands as they arrive.
The kernel module keeps track of 16 different "modes", or tightly repeating patterns. For instance, there can be a mode that toggles everything off and then on, and anothoer mode that strobes the lights up in a sequence (light one, then light two, then light three, etc.), and anther that produces a seemingly random light pattern. These modes can then be used to make larger, more complicated sequences we call "programs", which may contain loops and other higher-level niceties. An element of such a "program" is something we call a "tuple", which is the three-tuple of a length, a period, and a mode number. Further in depth explanations can be found on the Protocol page.
The translator unrolls all non-infinite loops, and passes the simplified (yet equivalent) program to the kernel module, along with all mode commands. Each command is sent to the kernel module separately.
The kernel module uses two timers to handle the light switches -- one for the changes within a mode, and another for changes between modes. These timers are interrupt-driven, and will have (much) less than a 1/100 of a second precision. Whenever the program timer goes off, it stops the current mode, changes to a new one, and starts the new one. Writing a new program to the kernel module will stop the old program (if it's running) and start the new one. However, writing a mode command to the module while it's still running will simply replace the mode, and the program will use the new mode (if it uses it at all).