Go at Amped 2010
About two months ago I attended Amped “The Hack Day: Reloaded” at the Powerhouse Museum in Sydney. A group of developers got together and formed teams to compete in an array of challenges. Among the challenges was to build something using the Powerhouse Museum API which they had unveiled that morning. I chose to take that challenge, and competed as a team of one.
Earlier in the week I had written and released a back-end for Rumpetroll, an engaging and unusual web experiment created by a group of scandinavians (“with love”). Rumpetroll is a canvas and websocket-driven web site with a simple concept: you control a tadpole that can swim around in a shared environment with others.
My first thought on seeing it was that Go would be ideally suited to powering its back-end. Go has a websocket package in its standard library, and its concurrency primitives are perfect for this kind of application. My implementation was about 160 lines in all (50 lines shorter than the original Ruby/EventMachine implementation).
My take on the Powerhouse challenge was to extend the Rumpetroll interface and my back-end to provide a collaborative interface for exploring the museum’s collection. There was a small amount of JavaScript programming involved, but the bulk of the work was in Go. I wrote a simple library for pulling data from the Powerhouse API, and extended the Rumpetroll back-end to retrieve museum items and serve them. You can see the result running at powerhouse.nf.id.au. The code is in the powerhouse branch of my Rumpetroll repository.
The basic functionality of the back-end works like this: each user has a websocket connection through which it sends and receives positional messages as the tadpoles move around the space. Each of those connections is handled by two goroutines; one for reading, and another for writing. The reader goroutine receives data from the websocket, decodes the JSON updates, labels them with a user ID, and sends them to a global muxer goroutine on a single “Incoming” channel. The muxer receives messages from all clients and re-sends them to each client on their own, unique return channel. Each client’s writer goroutine receives each message from its unique channel, JSON-encodes the message, and writes it to the websocket connection.
In then mapped the museum’s items in two-dimensional space. A ContentLayer goroutine listens to the incoming messages, and, when a user’s tadpole is within range of a content item, sends the item’s data to the user.
The whole thing was startlingly easy. A few hours in, I found myself grinning widely as I coloured in the necessary pieces of code. There were no dead ends; whenever I had to change or extend something, the path forward was obvious and painless. Even when doubling back on fundamental design decisions, Go’s lightweight syntax made my life easier than it would have been in other languages.. The only time I really spent swearing was at mysterious bugs in the JavaScript code (mostly typos that would have been caught by a static typing). Perhaps most telling of all is that I’m not even embarrassed by the rushed code I produced under pressure, even two months later
I look forward to the next event of this kind so that I may once again use Go as my not-so-secret weapon.