Daniel Morgan

Kickstarter's Year in Review

This is a mostly technical post about how I converted flat designs to an interactive microsite for Kickstarter in 2016 to feature all the good stuff the KS community accomplished that year. In 2009 I lived in San Francisco with my girlfriend Willa. We had both been fans of Kickstarter for a while, and knew a few people who had run successful projects. After a fun visit to the Kickstarter theatre to watch the World Cup, Willa felt like it would be a great place to work in arts-adjacent capacity, and she built herself a position as Kickstarter's first curator. Though this was prior to Kickstarter entering Public Benefit Corporation status, we both knew that it was a special kind of company to match people with creative projects. We moved to Brooklyn and she joined up, while I continued working remotely for Tumult now as the 'east coast satellite office.'

In my work at Tumult, I help people get their ideas into interactive animations for the web. I've worked with museums, ad agencies, freelance web developers, schools, and huge development teams at large companies to remove technical hurdles so their animations work. Usually folks are 90% of the way there, but there's a outlier browser or odd technical issue holding them back. The web can be a difficult place to create interactive content when you must support infinite devices, browser versions, and network speeds. So it was kinda scary when Kickstarter asked if I help develop their year in review mid-December of 2016. I knew this was a page that would hit the web hard and I would need to build out pretty wild animations that worked on devices old and new, large and small, and for users on slow internet speeds.

As I received the assets for the project and started testing how I would go about building out the animations I started to have a bit more confidence that I could do all the pages + designs they were going for. There were a few slides with animated text over videos that I was nervous about, but I realized I could get pretty good support for what the design team at Kickstarter and Gretel was going for.

Building the Year in Review

I thought I would go through some of the techniques I leveraged to build it out:

(First, you may want to go through it here: https://kickstarter.com/year/2016/)

Tumult Hype was the engine behind this entire project. I used its built-in responsive features, its retina image handling, and animation tools to first drop in the high quality imagery for the project. For some elements, I needed to cut out layers and move them around to adapt to responsive screen sizes. Much of the work I did in the attic of a farmhouse in Vermont, almost out of reach of the wifi during the holiday break...

Because I needed really solid and predictable finger and gesture detection, I decided to use Hammer for touch detection — Hammer listens for swipes on the element and I switch to the next scene based on the direction. There was a point where I tied upward swipes to a scene transition from the bottom to the top but removed it in favor of R --> L movement only.

Animations

For line animations, I created super tiny SVG paths with the help of Sketch and the SVGomg plugin, and then either animated their movement or appearance on the page.

Setting non-scaling-stroke on SVGs really helped here because I wanted them to always be the same width whether shown in my mobile or desktop layouts. For the #Transformation scene, the SVGs are increasing in size without scaling in thickness.

The Creative Independent Logo is a CSS animation on an SVG path I traced in Sketch from the original logo with the pencil tool, which expands over 1.66 seconds with.

Optimizing GIFs

I used the convert command line tool to initially build out my animated GIFs used mainly for Mobile devices. When starting from a movie, I used commands like:
convert -delay 2 -loop 1 0030_Projects_Sequence_No_Type.mov 0030_Projects_Sequence_No_Type2.gif

This give me a GIF with frames 2 milliseconds apart that plays once.

I then did color correction in Photoshop and further optimized the image to remove extra colors and adjust timing. Keeping the background always #F2EFEA proved challenging because Photoshop requires re-locking GIF colors in the Save to Web panel each time :( .

Initially I used an interesting tool called Gifrocket to convert movies into GIFs, but its output was too large. Today I might use Gifski.

After getting a GIF I wanted, I dropped it into Imageoptim to squeeze more bytes out.

Videos

MP4s, thankfully, have great support on the web now. We can support the vast majority of devices and browsers with h264 video. I set poster images on all videos to ensure people don't ever stare at a blank screen, used the 'playsinline' and preload="auto" attributes to make sure that videos do the right thing.

When appropriate, I set the poster image of the video to be a 1x1 pixel transparent GIF so that the page background showed through. Since that page background had the same responsive properties set on it, this resulted in a smooth transition to the first frame of the video.

Encoding videos with streaming hints helped remove any wait period.

Syncing Videos with animated text

I played around with a few ways to do this and ended up starting the video going, and listening to the current time of the video a bit farther into its load time. I wanted to make sure the video was really ready to go (even on slow connection) and that the text animation should be started. So this used an event listener for the current video time, and then started the show once the video had played a bit:

videoelement.addEventListener('timeupdate', function() {
var currentTime = whale_mobile_el.currentTime;
if ( currentTime >= 2.8 ) {
// start the text animation.
}

For the cursor replacement on desktop, I created an element (with an onclick handler) that fills the left and right spaces of the slides with a hover state:

.rightAreaBlack:hover {
cursor: url('https://d3mlfyygrfdi2i.cloudfront.net/year/2016/index.hyperesources/ArrowRightBlack.png'), e-resize !important;
}
.rightAreaWhite:hover {
cursor: url('https://d3mlfyygrfdi2i.cloudfront.net/year/2016/index.hyperesources/ArrowRightWhite.png'), e-resize !important;
}
.leftAreaBlack:hover {
cursor: url('https://d3mlfyygrfdi2i.cloudfront.net/year/2016/index.hyperesources/ArrowLeftBlack.png'), w-resize !important;
}
.leftAreaWhite:hover {
cursor: url('https://d3mlfyygrfdi2i.cloudfront.net/year/2016/index.hyperesources/ArrowLeftWhite.png'), w-resize !important;
}

If for some reason the browser doesn't support custom cursors, I'm using the Right and Left arrow cursors that come standard. (e-resize and W-resize).

These tools were essential:

Testing

I wouldn't have caught little bugs if it weren't for Browserstack. It especially helped me discover video quirks in different browsers that helped me sync up the text to video.

I spent a lot of time in Chrome's network tab to make sure that my content preloading ideas made sense: I do a lot of image preloading a few scenes before I need the image. On scenes without images, I preload large GIFs used later in the scene.

GIFs that didn't loop were tricky: I wanted to make sure that each time you saw the image, it played at least once. Unfortunately this required a redownload of the image each time using this code:

var GIFon0030img = document.getElementById('0030GIF')
GIFon0030img.src = GIFon0030img.src.replace(/?.*$/,"")+"?x="+Math.random();

This ensures that the gif plays once, but it also replays when you reopen the scene: and downloads an image with a query string at the end like:

https://d3mlfyygrfdi2i.cloudfront.net/year/2016/index.hyperesources/Mobile_0030_Projects_Sequence_No_Type2.gif?x=0.14602782593974828

So whenever you switch to a 'scene' containing this image, it would play a single time:

When preloading an image like this, browsers don't give you any information about whether the GIF has played yet, so this is the safe way to restart the image. Luckily this is only 180K and quite long so most device connection speeds can play it without stuttering.

That's about it!

by Daniel Morgan, tagged with: