Getting out the Vote with a JS snippet

On October 11th I released Vote Banner, a JS snippet that helps people exercise their voting rights and access relevant voting information.

vote banner

In the 3.5 weeks leading up to the election, it was loaded 1,087,248 times and seen by about 257,000 unique users, far more than I would have expected for a simple project such as this.

The impact

What was the impact? Hard to say. A quarter million people viewed it. Maybe it helped remind some of them to make plans to vote. Maybe they reminded friends and family. We can't measure these things.

The CTR on the banner was quite low, 0.3%. That means about 678 people actually clicked through to register to vote or look up their polling place (clicks were evenly split between both options).

I'm not hung up on the low CTR, though. That's better than ad CTR on some websites. Blanket political calls to action don't perform that well, a phenomenon that I've noted before with the Call Congress hotline. But every little bit counts.

Next steps

Vote Banner will go on ice until the 2020 election season. Next time, I'd like to release it ahead of time, including perhaps for primaries, and have it point to state-level data based on the user's IP address.

I'd also like to make distribution easier by e.g. making it a Cloudflare application, which can be added to your site in one click.

Interested in contributing? It's open source here.

Using analytics.js as a standalone library has developed analytics.js, a handy tool that allows you to send events to Google Analytics, Mixpanel, and many other services using one standard API.

analytics.js is open source but the documentation assumes you're using Segment's paid service.

There's a helpful post from Pivotal that describes self-hosted analytics.js, but its code contains a bug that causes events to not be tracked until analytics.js is loaded. Depending on how you load dependencies, this introduces a race condition. About 40% of my .page and .track calls were dropped as a result.

The Pivotal code attempts to record a queue of method calls before analytics.js is loaded. According to Segment's cofounder, the analytics.js library assumes it is loaded synchronously. As a result, analytics.load overwrites the queue and we lose our events.

Code walkthrough

First, create a queue and a stubbed analytics object. I separated the queue from the analytics object because we don't want the queue overwritten when analytics.js is loaded.

If you put this chunk before the rest of your JS, you can call,analytics.track({...}) without worrying about load timing.

The fixed code is available here under MIT license.

// Create a queue to push events and stub all methods || ( = {});  
window.analytics_queue || (window.analytics_queue = []);  
(function() {
  var methods = ['identify', 'track', 'trackLink', 'trackForm', 'trackClick', 'trackSubmit', 'page', 'pageview', 'ab', 'alias', 'ready', 'group', 'on', 'once', 'off'];

  var factory = function(method) {
    return function () {
      var args =;

  for (var i = 0; i < methods.length; i++) {
    var method = methods[i];[method] = factory(method);

This next part loads the script asynchronously. If you don't want to host analytics.js yourself, you could use cdnjs:

// Load analytics.js after everything else
analytics.load = function(callback) {  
  var script = document.createElement('script');
  script.async = true;
  script.type = 'text/javascript';
  script.src = '/assets/js/analytics.min.js';     // <--- your url here
  if (script.addEventListener) {
    script.addEventListener('load', function(e) {
      if (typeof callback === 'function') {
    }, false);
  } else {  // IE8
    script.onreadystatechange = function () {
      if (this.readyState == 'complete' || this.readyState == 'loaded') {
  var firstScript = document.getElementsByTagName('script')[0];
  firstScript.parentNode.insertBefore(script, firstScript);

Finally, in the load step, process the queued method calls:

analytics.load(function() {  
    'Google Analytics': {
       trackingId: 'UA-XXX-1'
     'Mixpanel': {
       token: 'XXX'

  // Loop through the interim analytics queue and reapply the calls to their
  // proper analytics.js method.
  while (window.analytics_queue.length > 0) {
    var item = window.analytics_queue.shift();
    var method = item.shift();
    if (analytics[method]) analytics[method].apply(analytics, item);

I'm releasing the fixed code under the MIT license. Have a look at the full code on Github.

Resources for the 2017 solar eclipse

More for my benefit than anyone else's, I've compiled these resources on the August solar eclipse.


The most detailed interactive map of the eclipse path is available here. NASA also has an official interactive map of the eclipse path.

Use Google Maps street view to figure out where exactly you're going to park and stand.


The total solar eclipse will last just a few minutes. Enter the zip code of your observation site in this calculator to determine exactly when the moon will cover the sun (this information is also available on the map above).

Is it going to be cloudy?

Eyeball this map, which is colored by average cloud cover.

Then use Weatherspark to look at historical cloud cover for your particular location. Drill down into the cloud diagram and select August 21 to get hour-by-hour historical predictions (example: Depoe Bay, OR).

Less Important Questions from StackOverflow's Developer Survey

The StackOverflow post on tabs vs. spaces made quite a splash yesterday with the revelation that people who indent their code with spaces earn more, on average, than people use tabs.

They've made their dataset available for download so I wrote a quick script to see what else we can learn about developer salaries.


There's not much difference.

Choice of editor

This is the one we've all been waiting for.

Ship it?

Fortune favors the bold.

Clicky keys

You probably should just talk to your officemate instead of using this result.

Open source helps

Toward this end, I've open sourced this analysis. I still don't really know my way around pandas/Jupyter, so maybe you can do better.

Building a better way for people to call Congress

This summer I created a phone number that immediately connects people with their representatives in the U.S. government: dialing 1-844-USA-0234 will forward the call to your senator's office. After they hang up, it automatically calls your next senator, and then your representative.

It's the lowest-friction way to contact a representative. No looking up phone numbers or entering your address on a web form.

This post discusses the usage patterns of the "Call Congress hotline" and steps I'm taking to make it more useful and mainstream.

Your Congresspeople

Who saw it?

Many people. It went viral on Facebook, Twitter, and Reddit. I don't know exactly how many people saw the phone number, but based on clickthrough estimates I've seen it was probably more than 100,000 people. My reasoning is that it made it to the Reddit frontpage (briefly, until it was removed by mods on /r/lifehacks) and the corresponding blog post received several tens of thousands of hits (I would estimate the CTR on social links is less than 10-20%).

The real question is whether virality actually translates into calls to senators and representatives.

By the numbers

In the six months it's been live, the phone number has directed about 1,500 calls to Congress. If we sum the duration of the calls, they amount to nearly 24 hours of solid time on the phone.

Number of calls per day since June

You can see the initial spike from the launch, which happened in the wake of the Orlando shootings. The second uptick is after the presidential election.

In June, ~100,000 people saw the number when it launched, but we did about 500 phone calls that week. That means the conversion rate for a call to action like "Call your Congressperson" is about 0.5%. On average, you'd have to show the phone number to 200 people on Facebook or Reddit before someone picked up the phone.

As a corollary, because the number of shares/likes/upvotes/retweets exceeds the number of calls, we can conclude that "click-to-share" activism is a whole lot more popular than picking up the phone, which would be much more effective.

Next, let's characterize the calls that were made. We don't have too much information on this because the calls are private. But we know how much time was spent on each call:

Histogram of call duration

As you can see here, the vast majority of calls were extremely brief, possibly test calls, people who got cold feet, or people who got an answering machine. In other words, not meaningful interactions with the Congressional office. However, it's entirely possible that a constituent can register their suggestion/complaint within 60 seconds, so maybe we shouldn't discount these entirely.

Even though there were many short calls, there were still significant numbers of higher-duration calls. These added up to many hours on the line with Congressional offices.

Who's using it?

As stated earlier, I don't have much info on my users. But I have anecdotal information.

Several groups in NYC are using the number to campaign against airport noise in their neighborhoods from JFK. They are well-organized and call their Congresspeople pretty regularly (they've also given me some good feedback - thanks!).

Back in July, Deray, one of the leaders of the Black Lives Matter movement, tweeted the number and generated a huge amount of traffic:

It popped up several more times as activists asked people to call Congress to allow immigration and reduce hate crimes.

But the number is used all across the political spectrum. It appeared several times on the now-infamous /r/The_Donald subreddit. In one such case, an activist called upon others to demand that Hillary Clinton be charged with treason. The phone number is cause-agnostic by design, and clearly there are many use cases.

The problem with a "Call Congress" hotline

I'm very happy that I helped facilitate even 1,500 phone calls, even if it's a drop in the bucket. Phone calls do make a difference, and are one of the best ways to send a message to your representatives' offices.

From the numbers, the biggest problem facing call campaigns is actually engaging people. We have seen that it's more fashionable to "share" your outrage on social media than to act upon it by picking up the phone, even if it's made as easy as possible, with all the dialing done automatically.

I think the missing ingredient is the organizers and leaders who inspire people and promote causes. This Congressional hotline is politically neutral. It doesn't come with a guide or script. It doesn't care about the cause you're calling in for.

This tool doesn't really belong in my hands. It belongs in the hands of organizers and others who can incite action. The situations in which this phone number has thrived have been when Deray tweeted it, the NYC airport groups shared it on Facebook, or people posted it on activist subreddits.

I looked into this more and learned that some major activist organizations don't actually have good technology options for grassroots organizing. Most work is overpriced, incomplete, and done by contractors.

Creating a tool for organizers

To solve this problem, I've created Speak Louder. It's an extension of the Congress hotline that lets organizers create their own phone numbers and landing pages customized to their specific causes.

Speak Louder

It also provides other neat tools like email and text message subscriptions, so you can supplement your social media outreach with direct communication.

This is similar to the Call Congress hotline, except now the call to action is attached to a cause. You can give people talking points, scripts, and other forms of encouragement. You can collect their information and follow up.

I theorize that this will lead to increased engagement in the long term.

Create a campaign

Create a campaign for a cause you care about

P.S.: it's open source, just like the phone number.


I don't know if Speak Louder will work out or if people will continue to use the Congress hotline (1-844-USA-0234!), but they matter to me a lot.

Big thanks to Twilio for supporting this project and giving me credit to continue running the service when demand spiked. (I'm going to continue to self-fund the Congress hotline, but may need to take donations to keep it running in the long term).

Lastly, thanks so much to everyone who shared this phone number and everyone who called in!