NPR’s Election Party app has a lot of moving parts. It displays live election results from the Associated Press, ingests posts from our Tumblr liveblog, bakes out visualizations of our data, and presents all of this in a slideshow that, on election night, was continuously changing through an admin. It even works as a Chromecast app.
All of the code is open source and freely available to read and use, but it can be hard to make sense of all of it without knowledge of our app template and all the things this app actually does.
There are countless little snippets of this app I could share, but I chose three pieces of the app that would be interesting to share in isolation.
Deploying bug fixes by reloading your users’ browsers
Our app was a static web page, as all of our apps are. We had a server separately parsing AP data, ingesting Tumblr posts and baking out the static website every few minutes, but the client never touched the server. This made it difficult to deploy bug fixes if something broke on election night.
To solve this problem, we devised a simple way to force every client to refresh the web page. We deployed a file with a timestamp to S3, and on page load, the client downloaded that file, read the timestamp and stored it. Then, every three minutes, the client would check that file to see if the timestamp had changed. If the timestamp had changed, the browser refreshed the page. Here’s the client-side code:
Locally, we could deploy the new timestamp file with a simple Fabric command and deploy function:
We used this once early in the night when we discovered an error with how we were displaying some of our slide types. It worked well, and we could assume all of our users were using the latest versions of our code.
Widescreen slides on any device
For our app, we decided to optimize for 16x9 or wider devices, which gets you most TVs, laptops, tablets and phones (in landscape mode). Fixing these slides to this aspect ratio and getting everything in the slides to size appropriately was tricky. We used an unusual technique to achieve this.
A demo of this is simple.
Your HTML file needs only a wrapper div and some content in it:
Then, in a CSS file, we set the base font size on the html element to 1vw and ensure there is no margin on the body:
On the wrapper div, we set a few critical styles to making this work, as well as some styles that make the demo visible:
In addition, we used rems for all other measurements, including font sizes, widths and heights, so that they would scale appropriately:
This, of course, required prompting users to shift their phones and tablets into landscape mode.
The functionality we desired for Chromecast users went beyond simple tab mirroring, which Chromecast allows you do to with any website. Instead, we wanted to make your casting device a remote control, able to mute audio and navigate between slides. To do so, we had to use the Google Cast SDK. The Cast SDK allows you to make the Chromecast load your app on an internal version of Chrome installed on the hardware.
The SDK works pretty well, and other people have done good work in documenting how to get a Chromecast app set up. Peter Janak, in particular wrote a Chromecast Hello World application that was very helpful for us.
To make our lives easier, we wrote a simple library to handle initializing Chromecast sessions and passing messages between the connected device and the Chromecast. Next time we develop a Chromecast app, we will probably develop this further into a standalone library, but it works well as is now.
chromecast_receiver.js. Read the full source here. These files provide a friendlier API for interacting with the Chromecast. Specifically, they define the
CHROMECAST_RECEIVER objects, which allow you to interact with casting devices and Chromecasts in code.
First, to setup a Chromecast app, you need to check if a user has the Chromecast extension installed:
An important thing to keep in mind is that, in our model, the Chromecast app actually runs the same code as the client. You need to maintain state across your app so that your code knows whether the client is a Chromecast or a regular web browser. Thus, you would have a function for the sender when a Chromecast session is initiated, and a code path in your ready function for Chromecasts specifically:
Note that you can set event listeners on the Chromecast. This allows you to send messages between the casting device and Chromecast, which powered our remote control functionality. Here’s an example message sending function and receiver callback that allowed us to mute the audio on the TV from the casting device:
Importantly, we were able to handle both casting devices and one-screen sessions in the same code path thanks to our state variables.
The many moving parts of our elections app created more interesting pieces of code, and you can dig through everything in our repo. As always, the code is open source and free to use.