Backlight in action
After playing with the Arduino and the LPD8806 LED strip for a while I hooked it up to the Arduino Ethernet (Arduino Uno with EthernetShield will also do) to build my own backlight for the iMac.

This is the part, which runs on the Arduino and accepts UDP datagram packages, parses them and does control the LPD8806 LED strip. I have a 1m with 32 LEDS, but that could be adapted.

// Includes
#include "LPD8806.h"
#include "SPI.h"
#include <Ethernet.h>
#include <EthernetUdp.h>

// Setup the Ethernet to receive UDP packets
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0E, 0x0D, 0x48 };
IPAddress ip(192,168,2,2);
unsigned int localPort=8888;
EthernetUDP Udp;
char packetBuffer[UDP_TX_PACKET_MAX_SIZE];
char ReplyBuffer[] = "ACK\n";
char Reset[] = "Reset\n";

// Memory for the color
#define RRR 0
#define GGG 1
#define BBB 2
uint32_t currentColor;
uint32_t oldColor;
byte cColor[3];
byte oColor[3];

// Setup the LEDstrup
int dataPin = 2;
int clockPin = 3;
// Set the first variable to the NUMBER of pixels. 32 = 32 pixels in a row
// The LED strips are 32 LEDs per meter but you can extend/cut the strip
LPD8806 strip = LPD8806(32, dataPin, clockPin);

// function prototypes, do not remove these!
void colorChase(uint32_t c, uint8_t wait);
void colorWipe(uint32_t c, uint8_t wait);
void dither(uint32_t c, uint8_t wait);
void scanner(uint8_t r, uint8_t g, uint8_t b, uint8_t wait);
void wave(uint32_t c, int cycles, uint8_t wait);
void rainbowCycle(uint8_t wait);
uint32_t Wheel(uint16_t WheelPos);

void setup() {
  // Start up the LED strip
  strip.begin();

  // Update the strip, to start they are all 'off'
  strip.show();
  colorChase(strip.Color(127,127,127),10);
  colorChase(0,0);

  Ethernet.begin(mac,ip);
  Udp.begin(localPort);

  cColor[RRR] = cColor[GGG] = cColor[BBB] = 0;
  oColor[RRR] = oColor[GGG] = oColor[BBB] = 0;
  currentColor = 0;
  oldColor = 0;
}

void loop() {
  int packetSize = Udp.parsePacket();
  if (packetSize && Udp.available()) {
    Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);

    if (packetBuffer[0] == 'c') {
      // Format: cRRRGGGBB
      oldColor=currentColor;
      for (int i=0;i<3;i++) {
        oColor[i] = cColor[i];
      }
      cColor[RRR] = (100 * (packetBuffer[1] - '0')) + (10 * (packetBuffer[2] - '0')) + (packetBuffer[3] - '0');
      cColor[GGG] = (100 * (packetBuffer[4] - '0')) + (10 * (packetBuffer[5] - '0')) + (packetBuffer[6] - '0');
      cColor[BBB] = (100 * (packetBuffer[7] - '0')) + (10 * (packetBuffer[8] - '0')) + (packetBuffer[9] - '0');
      for(int i=0;i<3;i++) {
        cColor[i] = (cColor[i] < 0) ? 0 : cColor[i];
        cColor[i] = (cColor[i] > 127) ? 127 : cColor[i];
      }
      currentColor = strip.Color(cColor[RRR],cColor[GGG],cColor[BBB]);
    }
    if (packetBuffer[0] == 'p') {
      byte Pos = (100 * (packetBuffer[1] - '0')) + (10 * (packetBuffer[2] - '0')) + (packetBuffer[3] - '0');
      Pos = (Pos < 0 ) ? 0 : Pos;
      Pos = (Pos > strip.numPixels()) ? strip.numPixels() : Pos;
      strip.setPixelColor(Pos, cColor[RRR], cColor[GGG], cColor[BBB]);
    }
    if (packetBuffer[0] == 'S') {
       strip.show();
       delay(10);
    }
    if (packetBuffer[0] == 's') {
      scanner(cColor[RRR], cColor[GGG], cColor[BBB],30);
    }
    if (packetBuffer[0] == 'w') {
      colorWipe(currentColor,30);
    }
    if (packetBuffer[0] == 'C') {
      colorChase(currentColor,30);
    }
    if (packetBuffer[0] == 'r') {
      rainbowCycle(0);
    }
    if (packetBuffer[0] == 'd') {
      dither(currentColor, 30);
    }
    if (packetBuffer[0] == 't') {
      byte diffColor[3];
      for(int i=0; i<3; i++) {
        diffColor[i] = (cColor[i] - oColor[i]) / 10;
      }
      for (int i=1; i<11; i++) {
        dither(strip.Color(oColor[RRR]+i*diffColor[RRR],oColor[GGG]+i*diffColor[GGG],oColor[BBB]+i*diffColor[BBB]),5);
      }
      dither(currentColor, 30);
    }
    if (packetBuffer[0] == 'R') {
      oldColor=currentColor;
      currentColor = 0;
      for (int i=0;i<3;i++) {
        oColor[i] = cColor[i];
        cColor[i] = 0;
      }
      colorChase(0,0);
      Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());Udp.write(Reset);Udp.endPacket();
    }
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());Udp.write(ReplyBuffer);Udp.endPacket();
    //Udp.flush();
    delay(10);
  }

}

// Cycle through the color wheel, equally spaced around the belt
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for (j=0; j < 384 * 5; j++) {     // 5 cycles of all 384 colors in the wheel
    for (i=0; i < strip.numPixels(); i++) {
      // tricky math! we use each pixel as a fraction of the full 384-color
      // wheel (thats the i / strip.numPixels() part)
      // Then add in j which makes the colors go around per pixel
      // the % 384 is to make the wheel cycle around
      strip.setPixelColor(i, Wheel(((i * 384 / strip.numPixels()) + j) % 384));
    }
    strip.show();   // write all the pixels out
    delay(wait);
  }
}

// fill the dots one after the other with said color
// good for testing purposes
void colorWipe(uint32_t c, uint8_t wait) {
  int i;

  for (i=0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
}

// Chase a dot down the strip
// good for testing purposes
void colorChase(uint32_t c, uint8_t wait) {
  int i;

  for (i=0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, 0);  // turn all pixels off
  }

  for (i=0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, c); // set one pixel
      strip.show();              // refresh strip display
      delay(wait);               // hold image for a moment
      strip.setPixelColor(i, 0); // erase pixel (but don't refresh yet)
  }
  strip.show(); // for last erased pixel
}

// An "ordered dither" fills every pixel in a sequence that looks
// sparkly and almost random, but actually follows a specific order.
void dither(uint32_t c, uint8_t wait) {

  // Determine highest bit needed to represent pixel index
  int hiBit = 0;
  int n = strip.numPixels() - 1;
  for(int bit=1; bit < 0x8000; bit <<= 1) {
    if(n & bit) hiBit = bit;
  }

  int bit, reverse;
  for(int i=0; i<(hiBit << 1); i++) {
    // Reverse the bits in i to create ordered dither:
    reverse = 0;
    for(bit=1; bit <= hiBit; bit <<= 1) {
      reverse <<= 1;
      if(i & bit) reverse |= 1;
    }
    strip.setPixelColor(reverse, c);
    strip.show();
    delay(wait);
  }
  delay(250); // Hold image for 1/4 sec
}

// "Larson scanner" = Cylon/KITT bouncing light effect
void scanner(uint8_t r, uint8_t g, uint8_t b, uint8_t wait) {
  int i, j, pos, dir;

  pos = 0;
  dir = 1;

  for(i=0; i<((strip.numPixels()-1) * 8); i++) {
    // Draw 5 pixels centered on pos.  setPixelColor() will clip
    // any pixels off the ends of the strip, no worries there.
    // we'll make the colors dimmer at the edges for a nice pulse
    // look
    strip.setPixelColor(pos - 2, strip.Color(r/4, g/4, b/4));
    strip.setPixelColor(pos - 1, strip.Color(r/2, g/2, b/2));
    strip.setPixelColor(pos, strip.Color(r, g, b));
    strip.setPixelColor(pos + 1, strip.Color(r/2, g/2, b/2));
    strip.setPixelColor(pos + 2, strip.Color(r/4, g/4, b/4));

    strip.show();
    delay(wait);
    // If we wanted to be sneaky we could erase just the tail end
    // pixel, but it's much easier just to erase the whole thing
    // and draw a new one next time.
    for(j=-2; j<= 2; j++) 
        strip.setPixelColor(pos+j, strip.Color(0,0,0));
    // Bounce off ends of strip
    pos += dir;
    if(pos < 0) {
      pos = 1;
      dir = -dir;
    } else if(pos >= strip.numPixels()) {
      pos = strip.numPixels() - 2;
      dir = -dir;
    }
  }
}

// Sine wave effect
#define PI 3.14159265
void wave(uint32_t c, int cycles, uint8_t wait) {
  float y;
  byte  r, g, b, r2, g2, b2;

  // Need to decompose color into its r, g, b elements
  g = (c >> 16) & 0x7f;
  r = (c >>  8) & 0x7f;
  b =  c        & 0x7f; 

  for(int x=0; x<(strip.numPixels()*5); x++)
  {
    for(int i=0; i<strip.numPixels(); i++) {
      y = sin(PI * (float)cycles * (float)(x + i) / (float)strip.numPixels());
      if(y >= 0.0) {
        // Peaks of sine wave are white
        y  = 1.0 - y; // Translate Y to 0.0 (top) to 1.0 (center)
        r2 = 127 - (byte)((float)(127 - r) * y);
        g2 = 127 - (byte)((float)(127 - g) * y);
        b2 = 127 - (byte)((float)(127 - b) * y);
      } else {
        // Troughs of sine wave are black
        y += 1.0; // Translate Y to 0.0 (bottom) to 1.0 (center)
        r2 = (byte)((float)r * y);
        g2 = (byte)((float)g * y);
        b2 = (byte)((float)b * y);
      }
      strip.setPixelColor(i, r2, g2, b2);
    }
    strip.show();
    delay(wait);
  }
}

/* Helper functions */

//Input a value 0 to 384 to get a color value.
//The colours are a transition r - g - b - back to r

uint32_t Wheel(uint16_t WheelPos)
{
  byte r, g, b;
  switch(WheelPos / 128)
  {
    case 0:
      r = 127 - WheelPos % 128; // red down
      g = WheelPos % 128;       // green up
      b = 0;                    // blue off
      break;
    case 1:
      g = 127 - WheelPos % 128; // green down
      b = WheelPos % 128;       // blue up
      r = 0;                    // red off
      break;
    case 2:
      b = 127 - WheelPos % 128; // blue down
      r = WheelPos % 128;       // red up
      g = 0;                    // green off
      break;
  }
  return(strip.Color(r,g,b));
}

This is the php script, which does convert a screenshot to a strip image, which gets send to the Arduino for display

<?php
//
//	Screen background glow for a 32 LED Belt based on LDP8806
//
//	(C) 2013 -mat- filid brandy, brandy@klammeraffe.org
//

// Debugging
error_reporting(E_ALL | E_STRICT);
define('DEBUG', FALSE);

// How to reach the Arduino Ethernet (or Arduino Uno with EthernetShield)
// This used UDP to send the data across
define('HOSTIP', '192.168.2.2');
define('PORT', 8888);

// Where to find the screenshot
define('SCREENSHOT','/Users/brandy/screenshot.png');

// Handle sending the data to the Arduino
function w($socket,$msg) {

	if (DEBUG) print_r($msg." ");
	socket_sendto($socket,$msg,strlen($msg),0,HOSTIP,PORT);

	$buffer = '';
	$i = 0;

	// Wait for ACK or a timeout
	do {
		if (!@socket_recvfrom($socket, $buffer, 516, 0, $remotehost, $remoteport)) {
			print_r(socket_strerror(socket_last_error())." ");
			$i=100;
		} else {
			if (DEBUG) print_r($buffer);
			$i++;
		}
	} while( ('ACK'."\n" != $buffer) && ($i < 40) );

	// If we ran into a timeout, cancel the whole program
	if ($i > 40) {
		print_r("Timeout\n");
		$msg = 'R';
		socket_sendto($socket,$msg,strlen($msg),0,HOSTIP,PORT);
		exit;
	}
}

// Read the screenshot and prepare the temp buffers
$screenshot = imagecreatefrompng(SCREENSHOT);
$pixelated = imagecreatetruecolor(16,8);
$strip = imagecreatetruecolor(32,1);

// 1. Pixlate the original screenshot with $width/32 x $width/32 blocks
$width = imagesx($screenshot);
$height = imagesy($screenshot);
imagefilter($screenshot, IMG_FILTER_PIXELATE,($width/32),true);

// 2. Resize the pixelated screenshot into a 16x8 size
imagecopyresampled($pixelated,$screenshot,0,0,0,0,16,8,$width,$height);

/*

	TTTTTTTTTTTTTTTT
	L              R
	L              R
	L              R
	L              R  => LLLLLLLLTTTTTTTTTTTTTTTTRRRRRRRR
	L              R
	L              R
	L              R
	L              R
*/

// 3. Generate the strip image, which will be send to the Arduino
// 3.1 the 8 pixel of left and right
for ($y=0;$y<8;$y++) {
	$c = imagecolorat($pixelated,0,$y);
	imagesetpixel($strip,8-$y,0,$c);
	$c = imagecolorat($pixelated,15,$y);
	imagesetpixel($strip,$y+24,0,$c);
}
// 3.2 the 16 pixel of top
for ($x=0;$x<16;$x++) {
	$c = imagecolorat($pixelated,$x,0);
	imagesetpixel($strip,$x+8,0,$c);
}

if (DEBUG) {
	// Save the interim steps
	imagepng($pixelated,'resized.png');
	imagepng($screenshot,'pixelated.png');
	imagepng($strip,'strip.png');
}

// Open the UDP socket with 2 seconds timeout
$socket = socket_create(AF_INET,SOCK_DGRAM,SOL_UDP);
socket_set_option($socket,SOL_SOCKET, SO_RCVTIMEO, array("sec"=>2, "usec"=>0));

// Read the pixels of the strip and send them to the Arduino
$width=32;
for($x=0;$x<$width;$x++) {
		$rgb = imagecolorat($strip,$x,0);
		$r = ($rgb >> 16) & 0xFF;
		$g = ($rgb >> 8 ) & 0XFF;
		$b = $rgb & 0xFF;
		// Adjust from [0-255] to [0-127]
		$r = $r >> 2;
		$g = $g >> 2;
		$b = $b >> 2;

		// Send color
		$msg = sprintf("c%03d%03d%03d",$r,$g,$b);
		w($socket,$msg);
		// Send pixel location
		$msg = sprintf("p%03d",$x);
		w($socket,$msg);
}
// Send display the change
w($socket,'S');

// Close the UDP socket
socket_close($socket);

This is how you get (a still slow) backlight running on an iMac. This could be easily adapted to Linux or Wndows.

while [ 1==1]
do
   screencapture -T0 screenshot.png
   php Screenshow.php
done

And here are some pictures of the installation:

Arduino Ethernet with LPD8806 Belt and USB2Serial The LED belt behind the iMac

This is the process from screenshot to pixelated to the strip version:

screenshot pixelated strip

Cheers,
-mat-