For Methods of Motion last week, our assignment was to create a storyboard for a character animation. Using all that we've been learning about giving personality to characters through their motion (especially the 12 basic principles of animation) we were supposed to outline a short, simple scene that would introduce a character.
I decided to tell the story of a simple wine key. Like everyone else, I can't help but see a human form every time I look at one of these guys. With its extended arm, upturned eager face, monopod leg, and arm-like motion the temptation to personify these is so strong, I'm surprised I've never seen one as an animated character before.
Once I had the idea of animating a wine key, I started thinking about the world a wine key might inhabit, its hopes and dreams. The storyboard I ended up with tells the tale of a wine key's epic journey out of the drawer onto the counter in order to fulfill its lifelong dream of opening a bottle.
Here's an animated GIF that will show you the storyboard in five second frames (may take a little while to load, it's a 1.5mb file).
For something I'm supposed to animate in a week, this story ended up a bit.. .complex. Hence, this week I'm working on exploring just the basics of bringing the wine key to life: how it will move around, how it will express emotion, how it will interact with a few objects in its surroundings.
I'll post the final animation this week when it's done. Wish me luck!
This is the current working draft of the "People" game my team is working on for Big Games. The Prisoner's Dilemma seeks to see if a group of prisoners can stick together long enough to pull off a daring prison break. In the process, they'll have to hold out against the crafty warden who works to divide and catch them.
Note: all the 'balancing' between the players and the warden in this game has been tuned for games with four prisoners as that's all we have available. Certain numbers such as nights of work needed to escape and points needed for parole points may need to be tweaked for larger groups.
Players
- 1 Warden
- 4-? Prisoners
Objectives
The prisoners have two ways to win:
- 1) Breaking out of prison
- 2) Getting paroled
The warden has two ways to win:
- 1) Parole a prisoner
- 2) Catch all the prisoners working before they escape
To break out of prison, the prisoners, as a group, must complete (6) nights of work on their escape. Each night, each prisoner has the chance to work on the escape or not. For each prisoner who works without getting caught, one night's work is completed.
If all (6) nights of work are completed, all surviving prisoners win and the game is over.
In order to get paroled, a prisoner needs to receive 3 parole tokens from the warden. The warden can give these out each round in exchange for information about who's working.
If a single prisoner is paroled, the game ends, that prisoner wins and the warden also wins.
The warden also wins if all the prisoners have been caught working without any of them getting paroled.
Playing
Each day, while the warden is sequestered in his office, the prisoners formulate a plan. They decide who will work and those who will place a work token secretly in front of them.
Once they have decided who is working and who is not, each prisoner, one-by-one, goes to visit the warden for a private conversation. If the prisoner and the warden come to an agreement, the prisoner gives the warden information in exchange for a parole token. If the prisoner does not agree to accept the parole token, no information is exchanged.
The warden can only give out one parole token per day. If any prisoner receives (3) parole tokens, then that prisoner is paroled and wins the game. The warden also wins.
Once the warden has visited with all the prisoners, the warden returns to the yard and chooses which prisoner's cell to search. It the chosen prisoner is working that night, he is caught and out of the game. If not, he is safe and remains in the game.
With the warden's back turned, the prisoners who successfully worked now place their work tokens in public. If they have greater than (6), the remaining prisoners escape and the game is over.
If the prisoners have not yet escaped, they get a chance to investigate each other for collaboration with the warden. At the start of the game, each prisoner is issued a shiv. At this point in play, any prisoner with a shiv can accuse one of his fellows of collaborating with the warden. If the accused has received any parole tokens from the warden, he is shived and out of the game. Otherwise, if he has none, the accused takes possession of the accuser's shiv.
If the prisoners have not yet escaped and no one has been paroled, the day ends and the next day begins.
Here's that video I promised of our Living Art class performing Instructions for Indoor Music
Thanks, Morgen for uploading.
Last week, I gave a quick informative session to my fellow ITP students (a "Drive By" in our parlance) called "Don't Lose Your Shit: Version Control and Backups". I covered why you should care about version control, outlined the basic concepts, explained the difference between centralized and decentralized systems, did some some demos of using Git, and showed why GitHub is awesome. Then I did a quick five minutes on backups.
It was interesting to try to explain version control from scratch to a relatively less technical audience. I've taught git before to teams of developers who were already used to using Subversion in their daily work. This group ranged from people who had never even heard of version control to people who were comfortable with SVN and were mostly curious to learn about Git and GitHub, specifically.
I tried to be relatively straightforward about the differences between centralized and decentralized models, but I'm afraid my bias may have come through a little bit.
Anyway, in case it's useful to anyone else, I'm embedding the slides below. And, of course, the keynote and my notes are available on GitHub.
What can you make with simple rules? For our first assignment in Living Art, we were tasked with coming up with a simple set of instructions for the class to execute that would produce an interesting result.
A silly, but compelling, example is 241543903:

Google Image search results for "241543903" shows exactly how effective this set of simple rules has been at generating a lot of interesting activity.
Morgen and I decided to team up to see if we could create rules that would use music to explore a physical space. The idea was to use logic about where the performers are located in the room to differentiate them into different sound-making roles so that the resulting music would change based on the specifics of where it was performed. To accomplish this, we had to figure out a way to use a single set of instructions to differentiate the performers from each other based on their position in the room. To do this we came up with a series of if statements based on spatial position. The idea was that it might be complicated and confusing to read through the piece the first time, but once you'd identified which rule applied to you, actually performing your part would be relatively straightforward.
We also tried to give the piece some dramatic shape, to design something that would have a beginning, middle, and end. To accomplish this, we came up with the idea of giving the performers phrases to play that would phase against each other — musical patterns that repeated at odd intervals so that they would only line up after a number of repetitions.
The result of all of this thinking was Instructions for Indoor Music:
If you are sitting closest to the door begin stomping your feet in an even pulse. If you are the person closest to a corner clap on every stomp. If the person next to you is in the corner clap on every other stomp. If you are more than two people away from any corner say "ah" on every third stomp. If you are exactly two people from a corner radically change the height of your head on every seventh stomp. If you notice a stomp, clap, and "ah" happen simultaneously say "oh" on the next stomp. If you hear "oh" three times stop everything.
In the process of designing the piece, I put together a demo of what it might sound like using bloopsaphone:
In that example, the low tonal sound is being used to represent someone standing up. This demo also indulges in the fantasy that all of the players would start together and stay together, an idea of which we were suspect even in the design phase.
On additional detail worth noting: we tried to exclude any expectations or requirements for the space or the people in it so that it would be applicable to (and malleable by) any possible space. That's why the only feature of the space we explicitly mention is the door and why we went for the awkwardly phrased instruction of "radically change the height of your head" instead of just staying "stand up" — so that the piece could be performed in a room without chairs (although I'm noticing now that we do refer to the "person sitting closest to the door" so maybe that's a bug that should be removed as well).
When class came, we put the instructions up on the screen and people started clapping and stomping. As expected, different people started at different times, with the person closest the door figuring their part out first and kicking things off. Unexpectedly, people didn't necessarily wait to understand the conclusion condition before starting and they also struggled mightily to both play their part and listen to others simultaneously. The result was a bit of a chaotic mess with some performers thinking they saw the stop condition and hence ceasing while others still continued. We even tried to perform the piece a second time after the rules had a chance to sink in and while those results were better the effect was still very little like a group of people playing music together.
I think the (interesting) failure of this idea came from a couple of factors. First, the complexity of the many rules was just too high for people to grok. Since their situation in space might have meant that multiple rules applied to them and since everyone had to listen for the conclusion condition (the last rule) on top of that, it was just cognitive overload for a lot of people. Second, the way we presented the rules — all at once on screen — probably worsened this problem, not giving people enough time to let each rule (and whether or not it applied to them) sink in.
Morgen shot some video of the performance and I'll post a link to that sometime soon when he's got it online.
In the aftermath of class, I started thinking about a radically simpler complement piece to follow up on what we learned from this first effort. As the piece evolved and we started thinking of it as "Instructions for Indoor Music", that naturally begged the question: What would 'Instructions for Outdoor Music' look like? Here's a piece that tries to learn from the failures of Instructions for Indoor Music while also answering that question. Instructions for Outdoor Music:
If no one is clapping begin clapping. If more than one person is clapping stop clapping.
The thaumatrope is a Victorian-era optical toy. It uses the perceptual effect of persistence of vision to merge two images on the alternate sides of a rapidly spinning disk.
This week, for our first Methods of Motion assignment, I constructed five thaumatropes. Two of these used the two most common thaumatrope illusions: superimposing one image on another and combining two disparate images into a new whole. In the other three, however, I experimented with a different approach: using a thaumatrope to combine both sides of a stereograph in order to create a 3D effect. This second experiment met with mixed, but intriguing, results.
Finally, I built a lego prototype for a thaumatrope viewer. Normally, thaumatropes are viewed by rotating bits of twine attached to each end of the disk, causing it to spin end-over-end. However, this makes it hard to regulate the speed of the spin and forces you to change directions every few seconds as the twine uncoils. This viewer would allow you to play the thaumatrope by turning a hand crank in one continuous and comfortable motion, making it easier to control the speed of the spin and possible to play the thaumatrope without interruption.
First, my conventional thaumatropes. Since the thaumatrope illusion is created by having part of the image be absent on one side of the disk, I thought it would be appropriate to depict absence in the imagery I used as well. The first thing that came to mind on that theme was the Dodo.
This thaumatrope combines a photograph of a dodo bird skeleton in a museum case with an artist's rendering of what the dodo might have looked like. When combined, the painted dodo bird appears to have real photographed bones visible inside of it.
One technical lesson from this thaumatrope: to my surprise, the white background of the living bird blocked out the darker surroundings of the museum photo. I was imagining that the living bird would simply be superimposed on the photograph, but the white background altered the effect. If I had given the bird a black background, the superimposition might have worked better, allowing the light details from the museum scene to come through.
In my second attempt, World Trade Center thaumatrope, I used this effect of the white background to my advantage. Another obvious image that comes to mind on the theme of "absence" is the World Trade Center.
In this thaumatrope, I took two copies of the iconic photograph of the WTC towers from below and erased one tower from each copy, replacing it with a white geometric gap where the tower was removed. The effect when the images are combined is of ghostly flickering towers, as if the towers were both present and absent.
Now, the stereothaumatropes.
I started out with a stereographic print that I found on the web depicting women working in a stereograph factory:
I carefully separated both sides of this image and aligned them in Photoshop to create a two-frame animation that would create the 3D illusion by oscillating rapidly between the two, a "wigglegram":
Once I'd gotten the images lined up, I split them apart again and flipped one, working very carefully to ensure the alignment would stay consistent when I printed them out and assembled them (like all the other thaumatropes here, I had this one printed at Adorama photo in NYC, which I can't recommend highly enough; they give you 25 free prints for opening a new account and made really good quality prints from my uploaded digital files overnight). Stereograph Factory Stereothaumatrope:
The 3D effect is not as pronounced in the thaumatrope as the wigglegram above — largely because the thaumatrope is actually spinning so fast that the image blurs — but it is visible. And the effect is even stronger in person.
I produced two more stereothaumatropes in this same manner, one of the Old Faithful geiser:
(view Old Faithful Stereothaumatrope on YouTube)
and one of this rocky mountain:
(view Rocky Mountain Stereothaumatrope on YouTube)
In this last stereothaumatrope, as you can see, I experimented with an oval shape as it fit the image better and I hoped it would slow down the spinning to enhance the 3d illusion. This worked maybe too well, making this thaumatrope actually somewhat too difficult to operate.
Which brings me back to the idea of a thaumatrope player.
A design for such a player faces two main challenges. First, mounting the thaumatropes securely without altering them. An effective player would be able to operate on existing thaumatropes without spearing them with a shaft or creating any new holes in them. After some thinking, I realized this means needing to clamp onto them from the bottom and spin them around left-to-right rather than top-to-bottom.
The second challenge, then, is converting from the vertical motion that is natural for the human arm into a horizontal rotation to spin the thaumatrope. After some sketching, I came up with a mechanism that would use two 45 degree gears mounted orthogonally to transfer the rotation. Here's a video of a Lego prototype that demonstrates the idea:
Prototype lego thaumatrope player
The biggest problem with this design is that the thaumatrope only rotates once for each revolution you make with your hand. To make it possible to achieve faster rotation, the gear ratio between the crank and the thaumatrope should be higher: making the thaumatrope rotate more than once for every turn of the crank.
Unfortunately, the Lego kit is somewhat limited in its gear selection, but I was able to slightly increase the ratio in a later prototype:
I intend to keep working on this thaumatrope player in my Mechanisms class with an eye towards eventually building it with real gears and lasercut parts.
In the first session of Big Games this past week we undertook an exercise in rapid group game design. We were divided up into groups of eight or so, given two decks of cards and a box of six-sided dice and told to design a game using just those props in 15 minutes.
The main advice that Greg Trefry (the prof teaching the class) gave us was: play your game; don't theorize or go off into flights of fancy about how we'd expect our users to act, but just get something simple working and play it repeatedly while refining the rules and mechanic.
So that's what we did. After an initial flurry of ideas, we dealt out one full deck to everyone in the group and started working on a game that involved placing cards face down and bluffing. With each round, we added rules, twiddled mechanics, changed procedures to prevent ambiguity, and added constraints.
The 15 minutes ended just as we put the finishing touches on the game and the class reconvened to play our game. At first, it was difficult to explain to the group of volunteer players exactly how things were supposed to work. We'd built-up the rules one at a time while playing so they weren't logically arranged in our minds for easy consumption. But once we got the group playing it, they seemed to enjoy it, laughing along as players got caught in bluffs and dynamics developed. The whole class even stuck around five minutes after its scheduled end to wait to see who would win.
It was a fun and fascinating introduction to game design. I can definitely see how this process would be addicting.
I've written up the rules of our game below. Please let me know if you try to play it.
Call Out, a game of doubt. For dice, cards, and 4 to 8 players.
The goal of Call Out is to use luck and your ability to bluff — and to tell when others are bluffing — to get get rid of all of your cards before any other players.
Deal out all the cards in the deck evenly to each player.
At the start of each round, one player rolls two six-sided dice, generating a number from 2 to 12. This is the Target Number. In turn, each player places on the table face down some of the cards in his hand. If the numerical value of the cards[*] adds up to the Target Number this is a safe play. Otherwise, it is a bluff.
Once all the players have placed cards on the table, each player around the circle is given the option to Call Out other players: to accuse them of bluffing. If a caller correctly accuses another of bluffing, then the bluffer must take into his hand both his own face down cards and those of the caller. If the caller is incorrect, he must take both sets of cards.
Play proceeds in turn clockwise with each player still retaining cards in front of him having the option to either call a bluff or pass.
At the end of the round, the remaining bluffers place their cards in the discard pile; un-called players whose cards matched the Target Number (non-bluffers) return their cards to their hands.
The dice is rolled by the next player and the next round begins.
The game ends when one player runs out of cards. That player is the winner of the game.
[*]Aces are worth 1, face cards, 10.
The following was written to fulfill an assignment for Applications of Interactive Technology that asked us to ride the M5 bus its full length from Houston to Harlem.
I began the ride uptown in the early stages of sunset. As I waited at the corner of Houston and Laguardia, night settled in around the buildings, chilling the sky's pale blue hue. Across the street, a brick four story's fire escape made intricate cuts in what was left of the light.
The M5 arrived. Hydraulics cranked beneath its undercarriage as the bus settled itself into position at the stop. It unfurled a small metallic suspension bridge towards the sidewalk, tonguelike. Two elderly chinese ladies descended this, emptying the bus.
"Do you know where the Film Forum is?"
The slightly less wrinkly of the two had squared off in front of me, almost standing on my shoes. After extensive repeating of street names and some strategic pointing, I managed to get them headed west down Houston, the right compass rose point at least and the best of the sunset between the buildings down that direction.
When I got off the bus at the top of its uptown route, disoriented, motion sick, and half asphyxiated, this turned out to have been one of only two moments of human conversation in the entire course of the journey.
I boarded the bus.
After retrieving my metro card from the automated payment machine and nodding to the driver, I took the inner windowside seat of a row halfway down the bus's length.
I looked up at the seat back in front of me and was suddenly transported across six months and three thousand miles. With the seat back hardware filling my field of vision, every visual cue told me I was back on the 15 commuting from Belmont into downtown Portland, as I'd done every morning the previous winter. With its cylindrical protuberances, circular bolts, and molded plastic back, the M5 seat resembled Portland's Tri-Met's in every detail. Anxiety rose within me mixed with a surprising sentimentality. I saw visions of a brick storefront office with bearded faces peering out from behind computer monitors and yellow anoraks; I felt the preparatory cringe as my body resisted passing through the threshold to start one more day of meaningless work, waiting.
When I came out of my revery, I discovered six other passengers had boarded with me. Three of the four singles sat in solo seats along the right side of the aisle. The frontmost of these, a small gray-haired lady with scars in her deep black skin, had surrounded herself with a small fortress of crinkly plastic shopping bags.
Up front, in a double seat, a woman spoke Chinese to a small boy with thick glasses.
A bald black man sat directly in front of me. His ears were doubly surrounded by slim-rimmed glasses and round white earbuds. His jacket collar protruded over the seat back making small synthetic whooshing noises as the bus jostled, revealing a gold-zippered hood and epaulets on the shoulders.
It was getting darker and we were already in the 20s, heading north on Avenue of the Americas.
We stopped and stopped. Passengers got on, blinking as they entered the humid glow of the bus's interior. Others got off, swallowed up by the increasingly impenetrable darkness outside. The seats filled up and some new arrivals began to stand in the aisle. The windows darkened enough to reflect internal light, sealing us off from the street.
A woman in the lead single seat fiddled with a cherry red cell phone, extending an old fashioned antenna nearly a foot from its top. Her face remained invisible behind a deep blue babushka with a printed metallic design. She focused intensely on the phone (was it a radio?) twiddling knobs and rhythmically leaning in close. The device's noise didn't carry across the growing crowd in the aisle.
Despite the fact that we were now alone — the group of us in this private glowing bubble gradually floating north — we were not, somehow, together. People acted as if they were in private, conducting intimate conversations loudly with neighboring friends in shared seats or distant ones invisible over the phone.
Near the front of the bus, a man with a white short-trimmed beard, a receding hairline and glasses held a varicolored New York street map with one hand while talking loudly into a phone cradled between his ear and shoulder: "You know when she was talking about that hotel the other day? When she was talking about that, she said, 'I'd live here.'"
We turned left on 59th to dodge around the park before continuing north on broadway.
A row behind me a gaunt effeminate teenager was complaining loudly to his female seat companion, one eye continuously on his cell: "Why did you take those things off your profile? I'm just asking. Am I still in your top friends? I'm just asking."
It was like an invisible gap had opened between us that both isolated and protected us from each other. People lost their fear of being overheard along with their ability to communicate.
I could stare at the backs of these people's heads intently enough to draw them and I could eavesdrop on their conversations clearly enough to transcribe them, but actually speaking to them or looking them directly in the eye would have violated the social contract of our little glowing cloistered world.
Just as this realization was settling over me, though, something happened.
We'd made our first stop on Riverside Drive, the inky blackness of the Hudson broken by gothic shadowy shapes of tree branches in the left bank of windows. I looked up from the sketch I'd been making of a beret-clad earbudded twenty something staring out into the dark to find that my seatmate had been watching me draw.
He jerked his eyes away when he saw I'd caught him, adjusting his posture towards the aisle. But then he turned back and looked up at me sheepishly. He was a middle aged black man in a Boston Red Sox windbreaker with the red knit cuffs rolled back to reveal a chunky silver watch. His hands were crossed over the newspaper in his lap.
"Great drawings there," he grinned. "Are you a student?"
"Thanks. Yeah. I'm heading up to Columbia to meet some friends." My cover story.
"Did you do one of me?"
"No. Not yet. I'm mostly doing the backs of peoples heads."
"I see. Ok. Well, good luck. This is me."
And he was gone up the aisle, squeezing his way between two asian girls giggling to each other, heads leaned in close, and off at the next stop into the dark.
I sat quietly for a few minutes as the river sped past outside the window. The bus made stops and gradually emptied out, leaving just a handful of passengers as we climbed north of 100th. The smell of exhaust started to fill the cabin and a swirling motion sickness took hold in my guts.
Despite this, I started to draw again: a Chinese man with a checkered fishing hat and gray tufts of hair at his temples. But this time something was different. The back of his head didn't feel like a barrier. The distance had closed. As I woozily filled in the geometric pattern covering his floppy hat, I started to imagine fragmented images from this man's life: hot afternoons on a glassy lake somewhere surrounded by smiling, round-faced grandchildren in overalls.
We rode further north, passing under an elevated train, beyond any part of Manhattan I'd ever seen. The smell of exhaust fumes worsened, my stomach swooned, and my head spun.
The bus stopped and stayed still for a long time, mechanical noises issuing from just outside, the hydraulic ramp again in action.
Finally, a wide motor-powered wheelchair made its way onto the bus. Its pilot wore a stocking over a baseball cap brim and most of his face, giving his head the appearance of a lumpy sock puppet. He wore a shapeless garment with wide vertical stripes that concealed his body's basic outline. There was a canvas bag slung over his wheelchair's head with the curved black grip of a cane poking out. Everything about his physical presence was armored and other.
With the driver's help, he maneuvered his wheelchair into position, taking the place of the side row of single seats that had been flipped up to make space. The bus pulled back out into traffic and I watched the back of his head for a few stops.
Around 160th, my wooziness overtook me and I desperately needed to be standing still in fresh air. I stumbled my way to the front of the bus, brushing past the man in the wheelchair — seeing a patch of dark skin and a flash of white eye through a hole in his ski mask — and then burst out into the street at the next stop, frantically filling my lungs.
I walked east down the first street I found, trying to clear my head and get my bearings. As the bus pulled away behind me, I was still trying to imagine it, the life beneath that mask.
I've mentioned Clay Shirky's Design For UNICEF class once before: here. The goal of the class is to come up with ways of using the rapidly growing number of cell phones in sub-saharan Africa as a platform for projects that would improve the health, education, or security of young people.
My final project for the class was Infone, a tool for rapid surveying of local conditions by text message with incentives for participation. The idea is to send a regular stream of text messages to people in an area in which it's hard to gather data about living conditions, paying them with free cell phone credits. In these messages you ask simple factual questions designed to elicit information that might be useful for deriving facts such as road outages and the existence of conditions that might lead to disease outbreak.
For example, if you regularly ask about the existence of standing water in a village and suddenly you see an increase, that's a good reason to expect a rise in malaria in that area due to improved mosquito breeding conditions.
The project is designed to ask simple factual questions that are easiest to answer truthfully. That combined with the fact that we control who gets asked and how frequently should limit the danger of bad actors exploiting the system for more than their share of the free cell phone credits we plan to distribute to encourage participation.
We've gotten great feedback throughout the semester from UNICEF staff, Clay, and our fellow students. For more info, you can view the slideshow we presented at UNICEF and checkout our in-progress website.
RapidSMS
We're building our Infone prototype on top of RapidSMS, a framework developed by UNICEF for handling incoming text messages inside of Django. RapidSMS consists of three main pieces: the router, which processes incoming text messages and sends them to each application for handling; a library of existing applications that implement common functionality such as tracking users, sending credits, even bridging between SMS and IRC; and, finally, Django itself for building a web interface for non-SMS tasks.
RapidSMS is a great project that's surprisingly hip for something developed inside of such a large organization. For example, they use GitHub for distributed version control with a ton of forks. However, the project's biggest downside is its lack of clear and well-organized documentation. If we hadn't had constant access to the project's developers themselves inside of UNICEF through this class, I don't think we would have gotten nearly as far with it as we did in implementing Infone.
To attempt to partially remedy this last problem, I'm going to document here everything I've learned about using RapidSMS to develop a prototype SMS-handling application.
Installing Django
It goes without saying that in order to run RapidSMS you'll need Python and Django. For this, Django's install page is probably the best starting point. To my surprise, I struggled quite significantly with this stage and was taken aback by the lack of a consensus packaging system in Python. Somethings come as "eggs" some as tarballs with make scripts, some in other forms.
I'm not going to try to walk you through all of the steps of getting Django running as Google and persistence will provide better and more up-to-date instructions than I could hope to. I will, however, mention that one thing that particularly plagued me was incompatibilities between Python 2.5 and 2.6. I ended up simply pointing the Python in my Bash path to point at 2.5 to get everything to work smoothly.
Once you've got Django installed follow UNICEF's instructions for getting RapidSMS installed and configured. RapidSMS is simply a Django app with some additional infrastructure to allow you to connect to a GSM modem and to build convenient abstractions for handling incoming text messages.
Responding to SMS
Now, we're ready to start using RapidSMS. I'm not going to focus too much on the technical details of configuring and administering a RapidSMS install. Those are available in the project's documentation. I'm going to focus on explaining the RapidSMS architecture so that you can use it to build your own application.
As mentioned above, the first part of that architecture is the router. The router lets us handle SMS by implementing a single class with a series of methods that get called at each stage in the message life cycle: "start", "parse", "handle", "cleanup", "outgoing", and "stop". Each of these methods gets passed an object representing the current text message, including its content, the phone number from which it came, etc.
Before we can experiment with receiving messages, however, we need to create an app. Now that RapidSMS is installed, we can use its command line utility to generate our app:
rapidsms startproject test-project
Running that command will create a new directory inside of your RapidSMS checkout's app directory named "test-project". If you navigate in there you should see two files: test-project/__init__.py and test-project/app.py, which contains a stub of our text message-handling class.
Let's look at the app.py file that got generated for us:
Just like I mentioned above, here are six methods that each get passed an object representing the text message (they also get passed "self" as the first argument, which is a Python convention). They each also have a comment explaining when they get run in the router life cycle. From that, we can determine that the only method we really care about is "handle".
Let's take a look at an example of a handle implementation that actually does something. For this example, I'll pull code from the "echo" app. one of the built-in apps that ships with RapidSMS. Echo simply repeats back anything sent to it with "You said:" in front of it. It's useful for debugging and is probably the simplest demonstration of how the message object works.
This should be relatively self-explanatory. There are only two method calls with any substance. The first thing to note is that we retrieve the full text of the incoming SMS by calling "message.txt". Then, once we've done what we wanted with that (in this case, simply appending a string to it), we write back our response by calling "message.respond()" with our response as the argument. Simple.
(The one other tiny detail to note here is that this method returns True. As we'll see in more detail momentarily, each RapidSMS install can play host to any number of apps. When multiple apps are installed, you declare an order of precedence for them in a configuration file. If any app wants to prevent other subsequent apps from processing a message at any stage, it simply has to return False from the appropriate method in its App class.)
For many reasons, developing an SMS-based app while testing with actual text messages would be extremely annoying: having to acquire and setup a GSM modem before being able to get started, dealing with the vicissitudes of the GSM modem like message cache limits, the lag while waiting for each new message to arrive, not to mention using up your carrier text message allotments on both the outgoing and incoming sides.
Thankfully, RapidSMS provides an HttpTester app that lets you send test messages and receive test responses directly from the web interface. Assuming you didn't modify the default rapidsms.ini file, you should have the HttpTester app running. Visit http://localhost:8000/http and you should see a simple form with fields for "Phone Number" and "Message" (make sure you use HttpTester from Firefox; it uses some javascript that doesn't seem to work correctly in Safari (I haven't tested it in Internet Explorer, but I'd be highly surprised if it worked there either)). Enter a phone number (7 or 8 digits, no spaces would be most realistic) and a test message and hit "send". You should see the "Message Tester" log at the top of the page display your outgoing message and then the responses from any apps that are running (at this point, likely just echo).
Once you're seeing responses from echo, try adding your app to the list of installed apps in the rapidsms.ini file (under the "apps" variable around line 14 of that file; the order that apps are listed here will be the response priority as I described earlier), restarting your router with "./rapidsms route", and seeing if you get the response you're expecting from your app. Watch the log from your route process for errors if you don't get what you're expecting.
Using Apps as Libraries
Now that you've got basic SMS handling under control, the next step is to start building your actual application logic. The key tools for this work are Django models. Models are where you define your application logic: the structure of the data you want to store and the behavior you want to perform. Django models are abstractions on top of relational database tables, which they use for storage, so most of the code in these classes will be to declare the fields on each model (which correspond to columns in the sql table). You can read more about in the Django model documentation.
As a simple example, here's the Response model from Infone which we use to store the text messages that we receive from our participants:
The fields declared as foreign keys point to other models in the system (questions and respondents, respectively), the Char field holds the content of the text message, and the DateTime field holds the time at which the response was received.
Now, we could set about using Django models to build our entire application from scratch. But, if we did that, we'd end up solving a few common problems to SMS-based apps over and over again: recording and tracking respondents, aggregating poll questions, sending out credits, etc.
To prevent this problem, RapidSMS ships with a whole suite of existing applications many of which incorporate these common bits of functionality already. We can use this functionality in our own apps by simply importing these models into our own app and then constructing relations with them.
For an example, let's take a look at the real models.py file for Infone:
Notice, first of all the third line of this file: "from reporters.models import PersistantConnection, PersistantBackend, Reporter". With that one line we've brought over three classes from the RapidSMS "reporters" app. The reporter's app is designed to take in regular messages from a series of people out in the field. It has a lot of functionality that we don't need (such as the ability to classify reporters into different roles), but it has three classes that are a perfect fit for what we're trying to do with Infone: Reporter (which tracks personal details such as first and last name and phone number), PersistantConnection (which tracks the last time we heard from a reporter and what the best medium to contact them is) and PersistantBackend (which encapsulates a set of details for contacting reporters via different backends such as adapters for different GSM modems, or even via IRC or email potentially).
(Note: the misspelling of "persistent" as "persistant" is ubiquitous throughout the RapidSMS codebase; if you're looking for an easy patch to submit, going through and systematically fixing that would be a nice place to start.)
Once we've imported these models, the tables corresponding to them will automatically get created when we run "./rapidsms syncdb" right alongside the ones we need for our own models. And we can create relations with these models just like with our own. Look through the "register_from_message()" class method in our Respondent class for an example of how we use all of these classes together to accomplish something we need for our own application: automatically registering a new respondent from an incoming text message.
A really useful project for someone to undertake (either from within UNICEF or just a good-hearted volunteer) would be to systematically document all of the classes in all of the existing apps and write-up what they're useful for and any quirks or tricks associated with them. Currently, 35 apps ship with the main RapidSMS fork and if even half of them have useful models in them, that's a large and rich library that could be extremely useful to developers if its affordances were well understood.
Django Web Interface
For any non-trivial application, you'll probably want some kind of web interface in addition to the ability to respond to text messages. For example, Infone has an interface for administrators to create questions, assign respondents to them based on their past responses, and view results as they come in, including exporting them to Excel.
Again, the best place to look to learn how to build web interfaces in Django is the Django Documentation, but I'll walk you through the basic outline here of what goes where to get you started and provide some sense of an overview that can sometimes be hard to get from the detailed API documentation.
The two main files for defining a Django app are urls.py and views.py. In RapidSMS, these files live inside of your app directory alongside your app.py file, for example at apps/infone/urls.py and apps/infone/views.py.
The first of these, urls.py defines what URLs your application will respond to and what happens at each one. The second, views.py, defines a method to handle each url; this is where the requisite data is gathered from the models, form data gets parsed and used to create, update, or delete records, and the correct template gets selected for rendering. In the parlance of another popular web framework urls.py defines "routes" and views.py is the "controller".
Let's take a quick look at Infone's version of each of these to get a more realistic sense of what this means. First, urls.py:
Each line here represents a URL pattern and declares which view method should be called when that pattern matches. There are two basic types of patterns here: ones without wild cards and ones with wildcards. For example, this line: "url(r'^infone/questions/new/?$', views.new_question)" simply matches the url "http://localhost:8000/infone/questons/new" (with or without a trailing slash). When that url is requested, the view method, "new_question" gets called.
On the other hand, this line: "url(r'^infone/questions/(?P<id>\d+)\.csv/?$', views.question_csv)" includes a wild card for the "id" parameter. In other words, if someone requests "http://localhost:8000/infone/questions/1.csv", they'll get the CSV file for responses to question 1 and if they request "http://localhost:8000/infone/questions/42.csv", they'll get the CSV for question 42, but either way the save view method, "question_csv" will get called. The parameter will get passed to that view method as an argument, which we can then use to render the correct CSV (or do whatever is appropriate depending on the URL).
So, what do those view methods actually look like? The full Infone views.py is rather long, but here's an excerpted version that shows the implementation of the two view methods mentioned above: new_question and question_csv:
Note how the question_csv method takes two arguments (the request itself and then the "id" parameter) whereas the new_question method only takes one (the request).
I won't go through the details here, but you can see how the new_question method simply uses Django's built in form-rendering helpers to build a form for creating questions and then uses that to render the template (look for more about templates in the Django documentation). On the other hand, question_csv uses the Python CSV library to create a CSV file represent some data about the requested question and then renders that as an attachment so that the user's browser will automatically download it.
Django views can do a lot of additional things not demonstrated here and I highly recommend you spend some time with the Django view documentation.
Initiating SMS sending
One final note before wrapping this up. RapidSMS was designed to respond to incoming text messages, not to initiate sending outgoing messages. All of its infrastructure for sending messages is built to run in a loop that checks the GSM modem for incoming messages and then processes them when they arrive. However for Infone, we needed to initiate sending outgoing messages from the web form in order to send out our questions to registered respondents. This was exceedingly challenging under RapidSMS's current architecture.
Our eventual solution was to create a Target model as a queue of outgoing messages that need to be sent (see the models.py file I embedded above for the details). Then, we implemented our own custom backend that adds a method to the run loop to poll this table looking for new Targets that need sending. Take a look at the "send_next_message()" and "run()" methods in our infone_backend.py to see the code. Other than those two methods, it is identical to the generic RapidSMS GSM backend.
While this is not a perfect solution, it will do the trick for most applications and it is much simpler than running two copies of the GSM connection code (or some other workaround). We've talked some to the UNICEF team about adding this kind of message triggering functionality to the system as a whole, but since it bridges between the apps and the backend, we haven't yet hit on a way of doing it that is adequately modular.
Get Involved
Our deep gratitude goes out to the UNICEF team working on RapidSMS, especially Evan Wheeler, without whom Infone wouldn't have been possible. They've provided incredible support in class, on the #rapidsms channel on Freenode, and on the RapidSMS mailing list which are both great places to seek out support for your projects and to get involved with making RapidSMS better.
For the past year or so, Jem Axelrod and I have been working on a research project focusing on the history of computing. Last month, we presented the first product of that collaboration at the 2009 Pacific Ancient and Modern Languages Conference in San Francisco.
The paper was titled Free As In Beer: Cybernetic Science Fictions. It told the story of Project Cybersyn, an early 70s socialist pseudo-internet built by British cyberneticist Stafford Beer in Chile. We explored how Beer's writing, infographics, and industrial design worked together to create a science fictional narrative of omniscience and ominpotence for Salvador Allende's socialist government.
Recently, Jem and I put together a video version of this paper and put together a site for the larger project, which we're calling, Computer Science Fictions. The site is pretty basic for now, but we'll add to it as the research project progresses.
In working on the paper I've had a number of interesting conversations with various outside advisors including Clay Shirky who suggested thinking about the parallels with the East German Stasi, Tom Igoe who helped me work through some of the actual circuit diagrams in the Project Cybersyn documentation, and Matt Webb from Berg London who's in the midst of his own Cybernetics research project (much of Webb's thinking on the subject is beautifully summarized in his recent Web Directions South Keynote: Escalante). I just wanted to take a moment to publicly thank these three as I haven't had the chance to do so elsewhere.
We'll be continuing this project along many different avenues going forward including exploring the relationship between AI research and the early video game industry. For now, without further ado, the video:
Free As In Beer: Cybernetic Science Fictions from Greg Borenstein on Vimeo.


