Optimising the 2014 election results page

Once we had the design and back-end tech­no­logy run­ning, it was time to build the live elec­tion res­ults page it­self.

Counting day screenshot

Sever-side or client-side?

The key con­sid­er­a­tions here were speed and load. We wanted users to get the res­ults as quickly as pos­sible without over­load­ing our servers.

Generating pages on the server lets us cache the res­ults across users. This is how most pages at http://ibn.gramener.com/ were built when ana­lys­ing his­tor­ic­al elec­tion. But while this re­duces the load on the client-side, it in­creases the page size quite a bit. The al­tern­at­ive is to just send the data across and have the browser render it us­ing Javascript.

These visu­als are in SVG – so that already ex­cludes IE8 and older browsers. For the rest of the browsers, as long as we kept the cal­cu­la­tions sim­ple, there would be no is­sue in terms of browser cap­ab­il­ity.

What lib­rar­ies?

The easi­est way for you to ex­plore this is to look at its source. To be­gin with, here’s a snap­shot of the URLs loaded by the page. (This is a snap­shot taken on a mo­bile broad­band con­nec­tion to sim­u­late the re­l­at­ively slower modem-like speeds.)

URLs loaded and speed

After the main /live page is loaded, we load 6 CSS / Javascript files in par­al­lel.

  1. Bootstrap 3 CSS
  2. Bootstrap 3 JS
  3. jQuery 2
  4. Underscore.js
  5. Our cus­tom stylesheets: style.css
  6. Our in­tern­al Javascript util­ity belt: G.min.js

We did ex­plore the pos­sib­il­ity of keep­ing the page lighter and go­ing without these lib­rar­ies. But three things sug­ges­ted oth­er­wise. First, their size was only 54KB gzipped. In con­trast, the rest of the page + data was 285KB. Second, there still are too many browser is­sues for us to get to a good browser speed. Lastly, most users on this page would be re­peat vis­it­ors – so these lib­rar­ies would be cached the first time and do not add to the burden.

We chose Bootstrap many months ago for the elec­tion ana­lys­is page and con­tin­ued the choice here. Bootstrap re­quires jQuery.

Underscore.js, how­ever, was chosen for a single reas­on: it’s tem­plates. Ever since John Resig cre­ated a micro-templates, we’ve been a fan of this ap­proach. For ex­ample, to cre­ate this table of can­did­ates in each con­stitu­ency (and yes, there really where 11 Chandu Lal Sahus con­test­ing)…

Table of constituency results

… and to have it up­dated every time the res­ults change, this is the code re­quired:

Table micro-template

The key to the struc­tur­ing of this visu­al was to load all stat­ic data up­front, and min­im­ally re­fresh with the dy­nam­ic data.

Structuring the data

The only thing that changes dur­ing the elec­tions is the num­ber of votes for a can­did­ate. So, the­or­et­ic­ally, if we sent 8,000 num­bers as up­dates every time, the browser can cal­cu­late the rest.

In prac­tice, how­ever, the votes are not avail­able un­til much later. Only the win­ner / lead­ing can­did­ate is known. So we went for the a single JSON file that cap­tures the min­im­al data re­quired for dis­play.

Summary JSON

(Why JSON in­stead of CSV? Because it sup­ports hier­arch­ies, browsers parse it nat­ively, and when gzipped, it’s not much lar­ger than CSV.)

This file grew from ~20KB (when all votes were 0) to ~65KB (af­ter res­ults were de­clared). When com­pressed, this is just 27KB, which makes the up­dates ex­tremely light.

Summary JSON network

Filtering the data

Since all data is avail­able at the cli­ent side, all fil­ter­ing also hap­pens on the cli­ent side. To en­sure that the filtered URL is share­able, we use history.pushState to change it, but do the com­pu­ta­tions in-browser without ac­cess­ing the server.

The URL struc­ture of the fil­ter closely matches that of the data columns. For ex­ample, http://ibn.gramener.com/live?2014-Party=BJP fil­ters those con­stitu­en­cies where BJP is lead­ing in 2014. These fil­ters can be se­lec­ted mul­tiply – so you can see the res­ults of BJP + SS here – as well as in­de­pend­ently – so you can see where BJP won in Rural India. This makes it in­tu­it­ive to the de­velopers to add new fil­ters as well.

It’s all in the browser

As a res­ult of client-side ren­der­ing and fil­ter­ing, the only time the user ac­cesses the server is to fetch new data. We provided a re­fresh but­ton on the top-right, but for the an­chors present­ing on PPI device in the CNN-IBN of­fice, we needed an auto-refresh fa­cil­ity that would re­fresh at least once every 10 seconds.


So we built a secret ?refresh= para­met­er and ap­plied it on this device. We did not ap­ply auto-refresh in the morn­ing since we were wor­ried about the kind of traf­fic load that the site could handle.

But once the traf­fic star­ted de­creas­ing at around 9:30am…


… it be­came ob­vi­ous that a re­fresh of every 5 minutes wouldn’t hurt things, and would def­in­itely im­prove the ex­per­i­ence. We set it up to auto-refresh every 5 minutes by de­fault.

You can al­ways ex­plore fur­ther by ex­plor­ing the source at view-source:http://ibn.gramener.com/live.

Leave a Reply