Christmas with Processing

Over the holidays I got involved with the Cheerlights project by ioBridge Labs. The basic premise is that if you tweeted a colour to @cheerlights, it would appear on a series of feeds they were hosting, enabling all sorts of synchronised physical and virtual christmas light gizmo’s to be constructed. Coming latish to the game, I spent a happy afternoon on Christmas Eve knocking up a version in Processing. [all code here on GitHub.]

Background image.

Sketch 1, which  just consisted of a simple triangular tree, and one bauble showed that drawing an interesting tree was going to be a real pain, not to mention rather slow. So I went in search of a tree image I could use as a background onto which I could simply place the baubles and snow.  Luck led to the fine image above on the internet which was ideal. Now I could put it in a simple sketch.

Note: I needed a way to get the baubles positions. To avoid calculating it, I temporarily added the mousePressed() method and printed out the mouseX/Y co-ordinates to the debug screen while clicking on the right parts of the tree which I then cut and pasted back into the sketch.

Baubles.

I created a class of Bauble as an ArrayList of Points. Points is an inner class to handle the display of a particular bauble. There’s not much to Bauble; an add() method to add a Point and a display() method to run through the array and show the Points. They in turn have a show() method with a little bit of extra code to add a basic ‘twinkle’:

void show() {
int w = 10;
int h = 10;
if (frameCount % (int) random(4,10) == 0) {
    x = (int) random(4,10);
    y = (int) random(4,10);
}
ellipse(x,y,w,h); 
}

What this does is use the frame count to sure all the points don’t alter at the same time and each time one does it gets a random size. The effect is ok; but not quite like the real thing.

Snow.

SnowFlakes is again a class, with an inner called Flake. Flake creates a ellipse() of a random size and position on the screen. To make it look realistic each flake drifts slightly across the screen. SnowFlakes creates a (300) Flake array and then the fall() method runs through the array calling each Flake’s update() method. Again nothing too interesting apart from a little bit of code to add a slight drift depending on size of flake and slightly different speeds for each fall :

void update() {
 ellipse(xPosition, yPosition, flakeSize, flakeSize);
 //make them drift at slightly different speeds depending on size 
 if(direction == 0) {
 xPosition += flakeSize * 0.1;
 } else {
 xPosition -= flakeSize * 0.1;
 }

 // falls between 0.7 and 100%
 yPosition += (flakeSize * fall) + direction; 

 //reset if off the screen
 if(xPosition > width + flakeSize || xPosition < -flakeSize || yPosition > height + flakeSize) {
 xPosition = random(0, width);
 yPosition = -flakeSize;
 } 
 }

Untitled

At that point it was going pretty well. I’d some baubles and some mostly-realistic snow. The next step was to hook it up to the @cheerlights system. Initially, I was going to use the API text file to get the latest colour, until I realised from Twitter that the excellent @andysc had an MQTT version up as well.

MQTT+CheerLights

Now I’d done a previous blog post almost exactly a year ago about hooking up Processing and MQTT. Sill not updated to the latest library from IA92, but pretty sure it would still work, I was able to simply hook up that library to my new code to get the latest colour. The MQTTLib pde does the heavy lifting with a callback to send the new value to the Bauble class which gained a change() method. A bit of copy-paste to drop in the MessageHandler and add the right paths for the Cheer Lights MQTT broker/topic later I’d a working prototype which looks like so (support classes excluded).

//MQTT
import com.ibm.mqtt.MqttSimpleCallback;
private MQTTLib m;
private String MQTT_BROKER ="tcp://test.mosquitto.org:1883";
private String CLIENT_ID = "Tingenek23";
private int[] QOS = {0};
private String[] TOPICS = { "cheerlights"};
private boolean CLEAN_START = true;
private boolean RETAINED = false;
private short KEEP_ALIVE = 30;
PImage bg;
Baubles baubles;
SnowFlakes snowflakes;

void setup() {
size(640,480);
 bg = loadImage("snowtree.jpg");
 m = new MQTTLib(MQTT_BROKER, new MessageHandler());
 m.connect(CLIENT_ID, CLEAN_START, KEEP_ALIVE);
 m.subscribe(TOPICS, QOS);
 frameRate(30);
 noStroke();
 smooth(); 
 baubles = new Baubles();
 snowflakes = new SnowFlakes(300);
}
void draw() {
 background(bg);
 baubles.show();
 snowflakes.fall();
}

void mousePressed() {
 baubles.add(mouseX,mouseY);
 println(mouseX+","+mouseY);
}

private class MessageHandler implements MqttSimpleCallback {
public void connectionLost() throws Exception {
 System.out.println( "Connection has been lost." );
 //do something here
 m.connect(CLIENT_ID, CLEAN_START, KEEP_ALIVE);
 m.subscribe(TOPICS, QOS);
 }
public void publishArrived( String topicName, byte[] payload, int QoS, boolean retained ){
 String s = new String(payload);
 //Display the string
 println("New colour is " + s);
 baubles.change(s);
 }

We’re not done yet though. This works fine as a Java applet, but the latest Processing also supports an Android mode. Now you have to set this up, but once done, simply connecting up a phone, changing to Android mode and re-running, and I’ve got a version compiled onto my phone as well. [Remember to change ClientID before you run on your phone to avoid conflict with your desktop version]

Enjoy and have a great 2014!

Advertisements