Pirana: feeds for the reader

When the announcement of the “Responsive Edition” of An Event Apart’s 10k Apart showcase coincided with a little bit of free time on my calendar at the end of August, I figured: “Why not?”

One of the concepts I’ve had floating around in the big-box-of-ideas-that-will-probably-never-happen was the idea of a kindle-inspired RSS feed reader that would emphasize content before headlines. RSS is a great format for skimming the day’s news, but sometimes I just want to read. No ads, no digests—just me and a few thousand words with nothing but sound typography between us. It seemed like a good fit for the challenge.

Defining the problem

Build a responsive feed reader in under 10k? Piece of cake. But device compatibility and a weight limit alone can’t make an app. At least, not anything that anyone would want to use. So After a little bit of brainstorming, I settled on the key features I would want to see in any kind of reader:

  1. A focus on reading. Given the enormous volume of communications vying for our attention on a daily basis, it can be hard to take the time to just…read. To actually read. Not just to skim the surface of a few dozen articles or to flip quickly the day’s headlines, but to actually digest the words of writers practicing their craft.
  2. Offline support. A good story can’t help pass the time on the metro if it blacks out in the tunnel, so storage of stories for offline browsing was a must.
  3. Maintainability. 10k isn’t much, and I wanted to be sure it would be easy to build on the application down the line. Just in case.


To keep size down, Pirana ended up growing around a home-brewed microframework that implements only the barest bones of MVC development. Controllers and views are functions grouped into two hashes, and the basic model is little more than a collection bound to a basic localStorage adapter with a few essential methods:

  • set(key, object) – add or update an object in the collection
  • get(key) – retrieve an object from the collection
  • remove(key) – remove an object from the collection
  • each(callback) – call a function for all objects in the collection
  • keys() – retrieve an array of all keys in the collection
  • values() – retrieve an array of all objects in the collection

Routing is minimal, and a stripped-down event system limited to a few predefined callbacks rounds out the core of the framework:

var events = function(){
    var callbacks = {
    return {
        listen: function(evt, callback ) {  
        fire: function(evt, param){
            var i = 0,
            while(fx = callbacks[evt][i++]) {

A good excuse

Like most projects, Pirana offered a great opportunity to gain practice with some up-and-coming goodies. Besides the usual HTML5 semantics and some snappy CSS3 transitions, Pirana also takes advantage of:

  • Application caching to ensure offline support
  • localStorage for storing models
  • history for managing app state
  • canvas for drawing backgrounds


No, it isn’t spelled wrong. Grunge-rock afficianados will remember Chris Ballew’s lyrics on “Lump”, from the Presidents of the United States’ eponymous 1995 album:

mud flowed up into Lump’s pajamas,
she totally confused all the passing piranhas

That slant-rhyme has bugged me for a decade and a half, but with the Presidents unlikely to take it back the only reasonable option was to just adjust the pronounciation to match. Right?