Generative Visuals using Twitter for Resolume
by Eric Medine aka MKultra

This is a tutorial on how to create a Twitter feed in Flash for Resolume, using dynamically generated sliders and dynamic font control. You can load any twitter feed on-the-fly, and it will update automatically.

About Twitter
from Twitter.com:
Twitter is a real-time information network that connects you to the latest information about what you find interesting. Simply find the public streams you find most compelling and follow the conversations. At the heart of Twitter are small bursts of information called Tweets. Each Tweet is 140 characters in length, but don’t let the small size fool you—you can share a lot with a little space. Connected to each Tweet is a rich details pane that provides additional information, deeper context and embedded media. You can tell your story within your Tweet, or you can think of a Tweet as the headline, and use the details pane to tell the rest with photos, videos and other media content. See it in action.

In short:
Twitter is an RSS feed that is 140 characters long, maintained by a company that provides an easy way to search and manipulate all of its feeds.

How does it work?
You log into Twitter (or a third-party application) and write out some text. When you do that, Twitter sends a command using PHP to add the text to a node in an XML file that is tied to your account. You don't even need Twitter or another application to send Tweets-- you can write a PHP script that will do that for you if you like.

About Resolume
Resolume Avenue 3 is a real-time instrument for live (audio) visual performances. See all the features in detail and watch the getting started video.

About this tutorial
"Twitter for Resolume" involves building a Flash plug-in that can read a twitter stream, and then display it in Resolume for live text/twitter updates to your VJ sets. The tricky part is building enough error checking into the Flash plug-in so that it can break gracefully (more on that later) and enough control over the way that the text will display so that most any configuration can be accomodated.

Use this tutorial however you want, feel free to add to it, be sure to forward it to whomever and give it away for free. If you charge some lazy bastard for this tutorial, you suck. If you use it for a performance and you make money-- you rock! If you have some questions, advice, or praise, contact me at:
info(at)ericmedine(dot)com

What you will need
- Flash authoring environment or compiler.
- Resolume Avenue 3
- Internet connection

Other resources:
Resolume:
http://www.openprocessing.org/
Twitter API:
http://dev.twitter.com/
Downloadable FLASH and SWF files that can be plugged right into Resolume:
http://emaqdesign.com/video_loops_flash.html

STEPS:
1) Find a twitter feed you want to use, and write a string that will parse it
2) Build a Flash plug-in to load your string
3) Enable your Flash plug-in to display text from your string
4) Enable your Flash plug-in to interact with Resolume

STEP ONE: Find a twitter feed

Is this even a step? Most people view twitter feeds by an online application or on a mobile device, but we're not going to do that here. We're going to get up in the guts of how Twitter works, so we need to find out how to call to it. Luckily the Twitter API (http://dev.twitter.com/) tells us exactly how to do this.

And here is a string that will call to Twitter, and ask it to display some tweets from the user EMAQ Design:
http://twitter.com/statuses/user_timeline/EMAQdesign.xml?count=5

There's a bunch of other parameters we can ask for, but for now we're asking for the last five tweets. If you feel smart, you can check out the API and look for commands that will return geolocation info, various statuses, search parameters (return tweets with a specific text string or tag, for instance), etc.

No matter what name I put in there, I will get a correctly formatted XML file. For instance:
http://twitter.com/statuses/user_timeline/liveperformers.xml?count=5


STEP TWO: Build a Flash plug-in to load a string

Alright, open Flash up and build something!

// set up the XML lists that will hold our XML data
private var xmlData;
private var theTweetIndex:XMLList;


// load the xml
private function loadXML():void{
//// loads a twitter feed according to a user name set in Resolume

var loader:URLLoader = new URLLoader();

loader.addEventListener(IOErrorEvent.IO_ERROR, catchIOError);
loader.addEventListener(Event.COMPLETE, parseXML);

var urlReq:URLRequest = new URLRequest("http://twitter.com/statuses/user_timeline/" + _user1 + ".xml?count=5");

loader.load(urlReq);
}

You'll notice that we are LOADING the XML in two places-- once when the event listener "hears" that the XML is complete, and once when it "hears" that there's an error. This is so that even if the internet goes down, or there is some other problem with the XML file, the flash plug-in will not break-- it will load some back-up xml that can have an error message, or some kind of "station identification"

The reason we do that here is that "try/catch" will not work in this situation-- it executes faster than a loader works, so use the IO error checker to load a placeholder xml file ("xml/twitterPlaceHolder.xml") if the loader returns an error.

private function catchIOError(e:Event){
var loader:URLLoader = new URLLoader();
var urlReq = new URLRequest("xml/twitterPlaceHolder.xml");
loader.load(urlReq);
loader.addEventListener(Event.COMPLETE, parseXML);
}

private function parseXML(event:Event):void{
xmlData = new XML(event.target.data);
/// build the tweet xml list
theTweetIndex = xmlData.status.text;
//// returns all texts
// theTweetIndex = xmlData.status.user.name;
//// returns all names
// theTweetIndex = xmlData.status.user.id;
//// returns all ids updateText(null);
}

Besides a bad internet connection, what are some other occations when the Twitter feed might break? Well, there is a request limit set by the Twitter API of 150 per hour from the same ISP, so when you do these requests you have to be aware of how many you are doing. If you're over the limit, your request is met with a 404 error or something. Once an hour has passed without exceeding the limit, the twitter feed will be available again.

Every time you make a request to the API supposedly returns the current rate status to you in the response header.
You should look for the following keys:
X-RateLimit-Limit: 350
X-RateLimit-Remaining:
350 X-RateLimit-Reset: 1277485629

If you want to be sly, you can build a check into Flash that will stop any request if you're close to the limit.

STEP THREE: Display the Twitter feed in a text box

You'll want to make a dynamic text box to hold the text
... or maybe multiple text boxes to hold multiple feeds? Your call!

First, define your formatting style....

//// define formatting
private function initFormatting():void{
format1.font = theFontStyle;
format1.bold = true;
format1.size = theFontSize;
format1.letterSpacing = theLetterSpacing;
format1.color = theFontColor;
format1.leading = theFontLeading;
format1.align = "center"; ///
}

Then put a loop in your code to generate text boxes and apply the formatting to each on. Where do the formatting parameters such as "the FontLeading" and "theLetterSpacing" come from? Wait and see!

for(var i:Number = 0; i < numTextBoxes; i++){

///// add text boxes and make sure they're empty

var theBodyText:TextField = new TextField();
theBodyText.htmlText = "";
theBodyText.embedFonts = true;
theBodyText.name = "theBodyText";
theBodyText.type = TextFieldType.DYNAMIC;
theBodyText.border = false;
theBodyText.autoSize = TextFieldAutoSize.CENTER; // this //
theBodyText.width = 500;
theBodyText.wordWrap = true;
theBodyText.antiAliasType = AntiAliasType.ADVANCED;
theBodyText.y = 5; //
theBodyText.height/2;

// this puts the text box in premade containers called "mctxtsub0" thru "mctxtsub4"

var theTextHolder = mctxt.getChildByName("mctxtsub" + i);
theBodyText.width = theTextHolder.textBground.width * .75;
theTextHolder.addChild(theBodyText);
theBodyText.x -= theBodyText.width/2;
theBodyText.name = "theBodyText";
theBodyText.setTextFormat(format1); /// this formats the text boxes with a text format object called "format1"
}

 

STEP FOUR: Enable Flash to communicate with Resolume

You need to build the interactivity by adding "float" or "text" parameters to the Resolume class, then assigning listeners to those parameters. Once that's done, the Resolume environment will spawn a text field that will emable you to add a different account name to send the request to.

Here's how to set up the Flash parameters that Resolume can see:

//import the Resolume communication classes
import resolumeCom.*;
import resolumeCom.parameters.*;
import resolumeCom.events.*;

private var rtxt:StringParameter = resolume.addStringParameter("@Name", "EMAQDesign");
private var fontSizeSlider:FloatParameter = resolume.addFloatParameter("Font Size", 0.25);
private var fontStyleSlider:FloatParameter = resolume.addFloatParameter("Font Style" , 0.75);
private var fontSpacingSlider:FloatParameter = resolume.addFloatParameter("Tracking" , 0.25);
private var fontLeadingSlider:FloatParameter = resolume.addFloatParameter("Leading" , 0.25);

Notice how I have a bunch of resolume Float parameters and one resolume String parameter. Do they look familiar? These are the parameters that are the values for "format1" as defined previously. Now I can adjust the tracking/leading and font style on the fly-- if some of the twits are really short or really long, I want to adjust the text boxes to fit.

This next block of code listens for changes to our Resolume vars-- when it detects a change, it either reloads the XML (but we only want to do that when you change the @name parameter, since that parameter tells our XML call which Twitter account to request) or updates the font style. You'll notice it re-assigns the formatting variables, then makes another call to update text-- this re-applies the formatting AND re-populates the text boxes with data from the XML file, just in case there's been a change or a new tweet.

public function parameterChanged(event:ChangeEvent): void{
if (event.object == this.rtxt){
_user1 = this.rtxt.getValue();
loadXML();
} else if(event.object == fontStyleSlider){
theFontStyle = fontArray[Math.round(this.fontStyleSlider.getValue() * 10)];
initFormatting();
updateText(null);
} else if(event.object == fontLeadingSlider){
theFontLeading = Math.round(this.fontLeadingSlider.getValue() * 100);
fontSizeDisplay.text = String(theFontLeading);
initFormatting();
updateText(null);
} else if(event.object == this.fontSpacingSlider){
theLetterSpacing = Math.round(this.fontSpacingSlider.getValue() * 50);
initFormatting();
}else if(event.object == this.fontSizeSlider) {
theFontSize = Math.round(this.fontSizeSlider.getValue() * 100);
initFormatting();
updateText(null); // fontSizeDisplay.text = String(Math.round(this.fontSizeSlider.getValue() * 100));
}
}

And here's the "update text" function. Notice how I empty the text fields before putting more text in.

private function updateText(e:Event):void{
//// find all the text fields
for(var i:Number = 0; i < numTextBoxes; i++){
var theTextMC = mctxt.getChildByName("mctxtsub" + i);
var theTextBox = theTextMC.getChildByName("theBodyText");
//// clear all the text fields
theTextBox.text = "";
theTextBox.text = theTweetIndex[theNodeCounter];
theTextBox.setTextFormat(format1);
theTextBox.x = 0-theTextBox.width/2;
theTextBox.y = 0-theTextBox.height/2;
}
if(theNodeCounter > 3){
theNodeCounter = 0;
} else { theNodeCounter ++;
}
trace(theNodeCounter);
}

There you go! There's a log more finicky tweaking (don't get me started on the headaches of embedding fonts), but this is basically the gist of it. The most difficult part is parsing out the XML in a way that will look nice when we get it into Resolume.

In terms of performance, this runs pretty smoothly, altho I've noticed that Rez kinda takes a shit, processing-wise, when there's more than 3 layers of flash executing code.