LegalTech® New York 2013 Twitter wall for LexisNexis®
The requirements consisted primarily of streaming live tweets onto two large LED displays at LTNY, and a series of iPads running a voting web app. We ran 5 timed contests throughout the 3 days of the tradeshow. Each contest consisted of a topical question, displayed in the top area of the giant Twitter walls. Tradeshow attendees could tweet an answer to the current contest question by including the #RealLawLTNY hashtag. Within seconds, their tweeted answer would appear on the giant Twitter walls using a fancy CSS3 3D flip effect. The tweeted answer with the most votes would be the lucky recipient of an iPad® Mini.
On the tradeshow floor, some LexisNexis staff carried iPads and interacted directly with attendees, giving them the chance to vote and win the secondary draw. The iPads displayed the tweeted answers for the current contest and allowed users to tap them, flipping over the tweet and revealing a simple voting form.
Having recently written McMillan's holiday site in my framework of choice, Meteor, and leveraging Twitter's streaming API for that project, I re-used what made sense to get this project done in record time.
The short story is that Meteor made this project possible. It could have been built any number of ways, but matching the features and especially the budget for this app, nothing else (to my knowledge) would have allowed me to write it without putting in some really long nights or cutting back on features.
Meteor: the good parts
This one goes without saying for those who are already familiar with Meteor's major claim to fame. Meteor excels at making real-time easy. Imagine being able to update a local database that's in your browser, which then automatically updates the master database on the server (which can reject that change based on rules you define, rolling back your local database automatically), and then pushes out this same database update to all other connected clients' local databases who are holding the same data. This is what Meteor does for you for free.
The main use of real-time in this case was to show tweets as they arrived. But other unusual use cases arose:
Pushing the server time to all connected clients: By having the server send the current time to all connected clients (every second) in a reactive data source, and then using it in place of typical calls to
Date.now, all representations of time on each connected client updated automatically. Simply put, showing a 5 seconds ago message for tweets magically came alive — they would just start to update themselves on their own (6 seconds ago, 7 seconds ago, and so on). Having the time come from the server ensured that the time was being accurately represented to all connected clients. This was especially important for the contest countdown clock.
Pushing states/flags to all connected clients: There were two different interstitial-type screens as part of this project. The first was what we called the "takeover" screen. At any point in time, I could (from my admin panel) click a toggle button that would have every single connected client render a large message on a full-screen red background stating "Winners are being announced at booth #102!". Every TV, iPad and both giant LED walls all lit up in red with the takeover screen, in unison, at the click of a button.
The second screen was on a timer that fired every 3 minutes and turned off after 8 seconds. This one was similar to the takeover screen but was more gentle in its color choice. It was simply a full screen ad that re-iterated the contest rules and prizes.
Having this ability to easily push state to connected clients isn't anything new. But down the line, once this pushed state hits the client and enters a reactive context, you get to have fun with it. It's not just a static value — it's part of the reactive space of your application.
Hot code push
A particular stand-out feature in Meteor, known as "hot code push", really shined at the tradeshow. Since we were literally sitting behind one of the giant Twitter walls for three days making tweaks in response to how it was playing out, it was incredibly convenient to be able to make changes, push them up to Heroku, and watch all the active panels automatically reload with the newest codebase. Some of these panels were TVs hooked up throughout the tradeshow floor, and it would have been a major PIA to visit each attached workstation and iPad and hit refresh for each one, every time I pushed up a change. Hot code push allowed me to react very quickly and confidently to changes.
Meteor: the difficult parts
I had written code to queue up tweets as they arrived from the server, and display them 10 seconds apart. I wanted to give people a chance to read what was up on the giant screen before showing newer tweets. Simply having to write this code wasn't a big deal, as this is what I would have done in any other framework. But in Meteor, the idea is that you shouldn't have to. But since I was going against the grain and forcing a delay, I complicated things.
This became a moot point when we realized the frequency of tweets wasn't all that high, so all that complexity could have been avoided. But it just as easily could have been a show-stopping requirement.
Of course, after the show was done and I thought on this issue further, I came up with a more elegant solution in which I would queue up the tweets on the server, and through the use of
Meteor.setInterval, insert them into the database in batches. If the server shut down in between intervals, you could lose those tweets being held in memory, but it wouldn't be too far a stretch to have this queue live in a separate persistent collection instead of an array in memory.
This is another quip about how complexity goes up when going against the real-time grain. The tweets made use of the CSS3 3D flip effect. When a new tweet came in, the tiles flipped over, revealing the new tweet. The issue is that while flipping, both the previous and new tweets must be on-screen at once. This gets awkward to manage. I took several stabs at it and got a decent solution going. This unfortunately took up a lot of my time, and the code complexity was way too high for my liking.
And again, I did think of a cleaner approach after the fact. I should have done two publishes, one with records 1-10, and the other with 2-11 (by 1-10 I mean 1 being the newest record and 10 being the 10th newest). On the client, I would then represent side A of each tile with records 1-10, and side B of each tile with records 2-11. Whenever new tweets came in, I would just have to toggle the flip class, and I think that would work.
Generally speaking, if you side-step real-time, Meteor will stop holding your hand.
The human aspect
In particular, this project couldn't have been booted up and left to run itself. It was inherently a social experiment for LexisNexis, so we needed to be on our feet and react to changes in real-time ourselves. @JaredAYoung had TweetDeck running on his laptop the entire time, curating the walls and interacting with those who did reach out via Twitter. This human element kept the Twitter wall interesting and genuine.
For my part, I was busy adjusting layout, and even creating new views entirely (the TVs were a last minute addition!). The client wanted more insight into how it was playing out, so I whipped up a simple analytics page, which of course updated in real-time.
Additionally, this project gave me a chance to introduce @ako977, a developer at McMillan, to Meteor. I hope to rope him in to another Meteor project sometime soon!
The client was extremely pleased with the entire experience. And I got a chance to put Meteor to the test in a rather high-risk situation — had something gone wrong with Meteor, I would have been really screwed. But it ran like a charm, despite Twitter going down for a few hours and the Internet connection generally being terrible.
If you have a project that you think could leverage Meteor, and are looking for a consultant, let's talk. If your requirements go beyond a single consultant, I highly recommend McMillan as a full-service creative agency — chances are you'll still get to work with me!