Technical Write-up: Scroll Linked Animations

(This post is about my thesis project. You can see it at letsfreecongress.org)

Let’s Free Congress uses D3.js, Underscore.js, PubSub, JQuery and a keyframing routine I wrote to achieve the scoll linked animation effects. The basic idea is as follows.

Part One: Where is the User?

All animation on the page are triggered by scrolling, using JQuery’s scroll event to figure out where the user is relative to the document’s height. I quickly learned to keep the code modular, and started using PubSub to broadcast the scroll position to animation components. All the animating components subscribe to the scroll channel, and calculates properties like position, size, etc based on the scroll position broadcasted.

$w = $(window);
$w.scroll(function() {
    PubSub.publish("scrollTop", $w.scrollTop());

Something like that.

Part Two: Keyframes

With PubSub we have a scroll position and an event to trigger change. The next question becomes how we transform scroll position data to parameters values like x and y positions or sizes.

I figured I would try to work backwards. Ideally, I would want to be able to specify “keyframes” (which in this case would be scroll positions) and the numeric value that should be associated with it. Imagine I want a bar that grows to 240px tall as the page scrolls down 500px, and then shrinks down to 10px tall as the page scrolls further. I want to specify that animation like this:


I want to feed an array like that into a function, and get a magical function back. That magical function should take any positive integer (i.e. scroll position) and return what the value should be at that given position.

I figured I needed two parts. One set of functions that calculated scaled values between each keyframe (i.e. tweening functions), and a function that determines which tweening function to use.

A tweening function is basically a scale function in D3.js. Something like:

var tween = [];
tween[0] = d3.scale.linear()
tween[1] = d3.scale.linear()

Of course, the actual functions were generated by looping through scroll-to-value pairs. The next thing I needed was another function that determined which tweening function should be used given the scroll position. Since I am not a clever programmer, I just did it with a for loop.

var index = function(frame) {
    // Loop thru each keyframe
    for(var i = 0; i < keyframes.length; i++) {
       // If the current keyframe is > scroll position,
    we've found the range we are in.
        if(keyframes[i].pixel > frame) { break; }
    return i-1;

To recap: We use the index function to determine which range we are in, then use the tween function to get the exact value we should have. The whole thing comes together like this.

return function(scrollTop) {
    return tween[index(scrollTop)](scrollTop);

Part 3: All Together Now

With PubSub broadcasting scroll position, and a way to create keyframe tweening functions, I can now animate an object precisely based on scroll position, by doing something like:

keyframes = [

var barHeight = frameMapFactory(keyframes);

Which then can be used to animate graphical elements on mouse scroll, like this:

PubSub.subscribe("scrollTop", function(msg, data) {
    var height = barHeight(data);

You can imagine how a similar process can be applied to affect position, opacity and sizes of SVG object using D3.js. I made use of this technique extensively in the Let’s Free Congress website.

Once I do some cleaning up, I’ll put the open source the code on GitHub. Do ping me @tonyhschu if you have questions or comments!


So my campaign finance reform website (a.k.a. my thesis project) has been launched for five days. What has happened so far?

Let’s start with numbers. Up to the end of Friday, there has been 16,682 unique visitors to the site. According to Google Analytics, 9,524 people scrolled to the bottom of the page. Thanks to my friends over at United Republic, I know that 2,670 people have signed the petition through site.

That’s about 16% of people who visit that site. That’s not bad.


According to the twitter counter on the page, there has been 1,409 tweets linking to the page, and around 4,500 likes. Twitter brought in the most traffic - 5,799 visits, which is slightly more than the 5,167 visitors Facebook brought.

What happened?

Let’s start with what I know for sure. Jeffery Zeldman tweeted about it Monday that morning, and that really kicked it off. Jeffery has a large following that is interested in web design - so for that audience the site’s design resonated.

Jeffery’s tweet was retweeted 44 times. That made the rounds and at some point Joe Trippi (whom I wasn’t aware of until this point) tweeted it to his one million followers.

… and that was retweeted 26 times, one of which was by Larry Lessig. At which point one of my secret missions was accomplished. [Achievement Unlocked: Tweeted by Lawrence Lessig.]

Over the next couple days it slowed down quite a bit, with occasional spikes here and there. It got picked up in visually, but not anywhere else I am aware of. I did notice it making the rounds in the Washington activists community. Folks from the Sunlight Foundation, my friend Szelena from Rootstrikers, and friends from Propublica call tweeted about it. On Tuesday the folks behind United Republic sent out an email to their mailing list about Lessig’s latest TED talk, and mentioned the site as well.

Other Responses

I have gotten quite a few emails about the site. A few questions about how the site was built. A few thank you notes. A couple email noting spelling errors, which I promptly fixed. A couple emails discussing potential collaborations. Two emails from my heroes (!) whom I shall not name - with congratulations and suggestions for improvement in a) typography and b) javascript performance.

No hate mail yet. Although there are a couple tweets asking where the money for the $100 per American scheme will come from. (It’ll come from tax rebates.) There’s also a couple tweets simply saying it’s a bad idea. (Guess we’ll agree to disagree.)


Overall I am fairly pleased. It hasn’t quite reached as many people as I had wanted to yet. My goal was to eventually reach 50,000 people with this, so I am only 30% of the way there. My conversion rate was a lot higher than I had expected, though I suspect that’s because I’ve reach people who are mostly sympathetic to the cause.

I haven’t landed on any major online hubs that I had hoped to reach, but I haven’t been very proactive about those either. I will try to reach folks at Upworthy, Buzzfeed and Digg next week, and see if I can get a broader reach there. I am also preparing a technical write-up about how I built the animations for the Hacker News and Visualizing.org crowd.

Let’s see where this goes.


(Obvious) Mobile Is Important, and Hard

Reviewing the analytics for letsfreecongress.org today, one pair of statistics really stood out to me.


The site had about 13,000 visits over its first two days, with roughly 30% of the visit coming from mobile/tablet devices, and the rest from “desktop” devices. The difference in bounce rate between desktop and mobile is stark. For desktop visits, the bounce rate is roughly 10%. The bounce rate for mobile visits is close to 85%.

I knew there would be a difference, but I was not expecting the difference to be so drastic. The site was designed to be a desktop first experience, with its heavy and large interactive graphics. The mobile experience is a fallback, and not a very graceful one at that. All the datavis animations that makes the site great is missing.

The trend on the web is that more and more traffic will be on mobile and tablets. The question for me becomes, how do we design datavis pieces for the mobile that converts just as well? What does interactive datavis on mobile devices even look like?



It’s better to own up to mistakes publicly, so here’s my biggest one yet.

The amount of campaign funding that the American Anti-corruption Act (AACA) makes available is capped at 7.5 billion. I naively took the $100 per citizen amount, multiplied it by the population of America (~300 million) to…

I am apparently a terrible fact-checker. Quite embarrassed, but owning it anyway.


Owning Mistakes

In the interest of owning my mistakes, here’s some of the feedback and corrections I’ve received on so far for letsfreecongress.org:

  • I spelled received wrong: i before e, except after c. (Multiple people pointed this one out. I’m quite embarrassed actually.)
  • There aren’t 300,000,000 voting age Americans. I should figure out how many there actually are. (thanks Stephen! Not fixed yet, but soon.)
  • I used “dumb” apostrophes. I really should have caught this one, coming from a design program. (thanks Jason!)
  • The pop-overs were not hidden properly on large screens. (once again, thanks Jason!)

There’s more bugs and improvements to be made I’m sure. Please do keep the comments coming!


Launch Plan

So, I think I’m done building.

The code works, the story seems clear to people. As a piece of design, it’s about as done as it is going to get at this point.

The next step is launch. I have this design piece that tells a story, how do I get it out there? My plan has a couple parts.

Part 1 - the Friends and Family Release

On Monday, I’ll ping my network of friends on Twitter and Facebook to start sharing it with their networks. There’s a number of friends and mentors I’ve met over the last year who I think will push this out for me. Releasing this way will probably be a little bit slow, but it will be good to get feedback and make final adjustments before releasing it more broadly.

Part 2 - The Network Outreach

On Tuesday and Wednesday I’ll reach out to bloggers and organizations who I know are interested in issues around politics and campaign finance, and ask if they would be willing to cover it. Here I’m thinking folks like the Sunlight Foundation, Fight for the Future, Center for Responsive Politics, Huffington Post, Upworthy, etc. Not sure how well it will go, but it will be worth trying anyway.

Part 3 - Follow Up Work

On Thursday, and in the days after, I will try to write a series of blog posts about the work, targeted at different audiences. The first one will likely be a technical write-up, about how I went about building the scroll-linked animations on the site. That I will probably pitch to Hacker News and other web-tech minded communities. The second blogpost will be a draft of my thesis presentation, outlining why I believe design can change politics. I will try to pitch that to design blogs and politics blogs. The third blogpost will be about the motivation and decision-making behind the work - a process summary. Hopefully the design community will find it interesting.

A couple more days to go. Hoping next week will be interesting.


Meta-Thesis Reflection: Fun vs. Effective

At some point around February I decided that the goal for my thesis project was to reach as many people as possible, and show them the ideas from Larry Lessig’s book. The result of that decision was that I pivoted away from the legislation ideas and the complex datavis ideas. Instead, I focused on narrative, storytelling, and optimizing for reach.

Lately I’ve been feeling like thesis has become a grind. I am not having nearly as much fun as I was when I was building the pure datavis projects. I am less productive too. I built the election 2012 datavis in less than two weeks. The Free Congress story has taken close to a month, and I am really still not happy with it.

I am beginning to recognize this as a trade-off. I’ve traded working on things that are fun, for things that (I hope) are effective towards a specific goal. Sometimes effectiveness and fun align, often they do not. Doing good work is hard, and it pushes you out of your comfort zone, and sometimes that experience sucks.

I guess I’m talking to you, future thesis’ers - be aware of the trade-off.


Sorry I didn’t have time to write a short version

Copywriting is pain. Interaction design microcopy is the culmination of a thousand little pains. Of the three key interaction design components of my thesis project (copy, graphics, code) it is by far the one that feel the most difficult.

So, what am I doing about it?

1. Frameworks and Constraints

Using frameworks to try to make the writing easier. I’m still working wint the framework Michael gave me originally: Intrigue, Problem, Solution, Benefit, Ask. 

Pasting bits of ideas into Twitter and making sure they are under 140 characters has, paradoxically, helped. It’s forced me to be concise, and consider different wording.

2. Rewrite

Writing the same things over, and over, and over again. Writing it, hiding it, and trying to write the same things, and hoping it mutates a bit in my head. Basically trying to artificially induce a survival-of-the-fittest kind of process.

3. Recast into another medium

When I get really stuck, I go put the writing into HTML, and look at it in the browser. Sometimes I’d typeset it, just so it looks different, and see it under a different set of constraints.

4. Be embarrassed

By which I mean show other people, typos and awkward wording and all. It seems the very act of watching another person read something, and noting where they seem to nod or pause is a great way to seeing whether your writing flows or not.

5. Procrastinate

Go do something else. This for me often means code, or other people’s thesis projects, or getting some food. Sometimes the background processes in the subconscious just needs time to run. Some time away from a task is not always a terrible shrinking of responsibility.


Messing with typography as exploration for visual language to be used in Let’s Free Congress.


Seconds and Pixels

Had a chat with Paul Ford about thesis, where the conversations turned to memes, banner advertising, and primally satisfying interactions.

I laid out for him the funnel metaphor that I had received from Albert Wenger. To widen the funnel as much as possible as Albert suggested, I needed interactive baits that grabs people in the span of milliseconds, and then intrigue them with gripping but incomplete story. I need micro-interactions that fed into the larger interactions I hope to host at letsfreecongress.org.

Paul pointed me to the realm of the banner ads. In 300x250 world of banner ads, every second and pixel count. To evoke recognition, intrigue and design in this domain requires amazing design discipline. Paul pointed me to the amazing work of Al Rotches, who has mastered the alchemy of motion graphics and text into tightly orchestrated sub-second animations.

It’s all about pacing, which in turn is about the bandwidth of our sensory systems. How much information can we take in at a time? How quickly? Can the chunk be sweetened with visuals and animations? Do the visuals and the words work together to convey the message?

Those banner ads gets pacing just right, and taps into how our subconscious sensory systems operate. The flash, the animation sequence, the colours, the faces - all the cues our visual systems love. All that, and the impeccable control of time in constructing that experience.

Not sure I’ll ever be good at this, but it’s worth thinking about.