The majority of this past week was spent building ViewFinder, which is a Rails app that leverages the Instagram API to enable users to guess where photos were taken and gamefies Instagram photo feeds. Building ViewFinder along with Erin Lee and Jesse LaRusso has been a pretty great experience; we got way farther than I had expected initially for the NYC on Rails presentation. Looking back at our Trello board, we defined our MVP for the presentation as an app where users could browse tagged photos and write in the address or intersection where they think the photo was taken. What we ended up with was much more than that, and more visually appealing than I could have imagined initially.
1) Brainstorming and ideation
The idea for ViewFinder came from the View From Your Window (VFYW) game that the blogger Andrew Sullivan has on his blog, The Daily Dish. If you haven’t read the Daily Dish, you should start (and pay for the privilege!). Sully is a coherent and reasoned blogger, and his writing covers a large swath of topics, from public policy and politics, to business, to arts and literature. For his VFYW game, readers will send him photos of their view outside their window and since his readership spans the globe, the photos tend to be from far-flung regions like Japan, Indonesia or Estonia. I’ve never played the game, because the amount of context clues provided by a VFYW photo are not enough for me to have a reasoned guess of where the photo was taken, given the global scope. However, I’ve always thought that the concept of the game was fun, and that it could be made more accessible to the average person by adding context, localizing the game and making it social.
We explored a few other data visualization ideas for the NYC on Rails presentation. Most of these centered on using the NYC Open Data API to build a data visualization or map. However, we couldn’t quite figure out what we could build that would be a great learning experience, be a functional product and, most importantly, actually be fun to build. We knew that we wanted to work with data and APIs in some form, do something with maps and build an the end product with a well-designed front end.
In the end, I proposed ViewFinder as our NYC on Rails presentation. Erin and Jesse seemed to warm up to the idea pretty quickly, and we brainstormed a lot of ideas and features that we could build. We ended up agreeing to focus on building out a location-based game, where users could guess where Instagram photos were taken by geographic area in New York. Initially, we had Union Square, Times Square and 30 Rock as our main areas, but those gradually evolved into Midtown Manhattan, Downtown Manhattan and Downtown Brooklyn.
2) Schema, design and gameplay
I was happy that I was able to apply various programming techniques and tools that Avi/Bob/Blake have covered over the past few weeks. The two that come most clearly to mind are metaprogramming and asynchronous background job processing. Metaprogramming is a really useful technique to write methods that write their own methods. They are most applicable to code abstract patterns that vary by the data they accept rather than the actual methods that are applied to the data.
The area where I used metaprogramming most actively was in designing the Instagram API calls and connecting that to the gameplay. For the Instagram API calls, there are a few different ways to access the API - you can search by tag, by geographic location, by user or by popular media. For each of these calls, I needed to filter the data to remove any images that were not geotagged, as non-geotagged photos would be useless in a location guessing game. Thus, in my Instagram wrapper class - which wraps the Instagram gem, which itself directly accesses the Instagram API - I have the following code:
1 2 3 4 5 6 7 8 9 10 11 12
“acts_as_locatable” at the end of the above code block is a class method that takes in an arbitrary number of arguments and is executed at runtime. For each argument, it creates a method with the name of the argument prefixed with “filter_”. Each of these filter methods strip away the non-geotagged results of any of the Instagram API queries defined by the parameters passed to the acts_as_locatable class method. These methods flow into the photo model, which then takes the results of the filtered Instagram query and saves them to the database.
1 2 3 4 5 6 7 8 9 10 11 12 13
Like in the first code block, “acts_as_instagrammable” is a class method that creates a new instance of the Instagram wrapper, calls the relevant filtered Instagram API call and saves the photos to the database. This way, I know that whenever I want to get photos from Instagram, I can use their method names (e.g., tag_recent_media) and know that what I ultimately get back are instances of my photo database, which will only include geotagged items.
Finally, the below meta-method is the crux of the gameplay, and queries the database for photos while simultaneously initiating a background processing job via Sidekiq to get more data from Instagram.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
The above block does a number of things, and is admittedly probably a decent candidate for additional refactoring. This meta-method creates a method for each game that is created. Initially, we just defined three games in a hash (not shown here) called LOCATION_GAMES, but eventually we think it would be cool for users to create their own games. For example, someone who is really into architecture or beautiful aboreal landscapes can tag photos on Instagram with #viewfinder and another tag and basically create themed games. Additionally, we imagine that this game can be replicated across most any geography, so we wanted to have a flexible design where you could quickly define the parameters of a game and have that flow into the application. Currently, games are defined within the context of a photo, but I think we are looking to break out games into their own game model and make ViewFinder more solidly object-oriented.
Anyways, the above code is activated when a user clicks on a location-based game on the ViewFinder landing page. For each game, the method will determine the game parameters (i.e., coordinates, radius and photo set size), go to the database and get a random set of photos that have not been guesesd by the logged in user, are tagged with #viewfinder or #vfyw and are within the defined radius of the coordinates. Simultaneously, the method will set off a background job to get more data from Instagram within the location parameters of the game. This way, as more people play and demand for a given game scales, the application should theoretically make more API calls and thus get more real-time data from Instagram, all without impacting the user experience as the arduous task of accessing the Instagram API and saving photos to the database is disassociated from the photos that are loaded on the front-end.
There are definitely many areas where we can do additional refactoring and clean up the code. One place specifically where I think we can do better is in using view helper methods more actively, as well as moving more logic to our controllers. With the NYC on Rails presentation coming up on Thursday, I’ll admit that we took a few shortcuts in the middle of the week just to get stuff working. In the next few weeks, I hope to go back through the code from start to finish and refactor extensively.
In terms of the front-end design of the application, we wanted to make the user interface highly visual and intuitive for new users to quickly get up to speed on the game. We went through several iterations of design that used various photo carousel options. We eventually rotated back to Twitter Bootstrap’s carousel, and I’m pretty happy with how the design ended up.
3) Technical challenges
Another challenge we faced was working with Instagram authentication and enabling users to sign-in via Instagram and display their user feed and friend feeds. This challenge tied in with the difficulty of designing a clean, intuitive user flow - from choosing a game all the way to viewing the results after making a guess. The first thing I tried to do actually when working on ViewFinder was to try and set up Instagram authentication. I used the Omniauth gem to do this, but it was a frustrating experience. I learned eventually that I was over-programming, in a sense, and needed to just let Omniauth take care of the authentication process. Building an intuitive user experience with Instagram authentication is still something we’re working on, and I think that making the gameplay more object-oriented will help in that regard.
4) Future extensions
I think it would be great to enable users to create their own games. Eventually, I’d like it for people passionate about sculptures, for example, to be able to take pictures across the globe and tag their pics with #sculpture and #viewfinder and have the application pick that up for their followers to guess where they’ve been. In general, I like the notion of themed games as a way to enable people that are passionate about whatever to better express that passion.
Also, it would be really cool if users could play timed games on ViewFinder, or challenge their friends to see who can best guess a set of 10 photos in a fixed amount of time. Another feature that I’d like to work on is to build out the analytics of the game and tap into the competitive nature of people. I’m a bit of a data analytics geek, and it would be super cool if we could use user data from the game to create cool data visualizations.
On the technical side, there is a certain amount of technical debt that got built up with the rush to deploy before the NYC on Rails presentation. I don’t think the debt is insurmountable, but the app can definitely benefit from some refactoring. Using view helpers more effectively, moving some code into modules for shared functionality and making gameplay more object-oriented are all changes that I think would make the codebase cleaner and the user experience more fluid.
I definitely would like to continue developing ViewFinder and building out new features. I’ve enjoyed the process of designing the product, working in a team and writing code. There’s an undefinable joy in having an idea and being able to execute on that idea and see the results. Like many creative ventures, coding is hard enough so that its always challenging, but its also accessible and progress is possible with concerted effort. And to steal a phrase from Avi, coding is all about taking complexity and delivering simplicity. A simple, sensible user interface hides the complexity within a codebase, and now being able to understand how that complexity creates simplicity has been fantastic.