Visualizing Web Design Evolution Using Git

My website here, as of the time of writing this, is still based on a design I made back in 2010, and is rendered using my static site generator that I haven't touched in nearly as long. The site's served its purpose pretty well, but it's kind of a mess; It's unreadable on mobile devices, the CSS causes some weird inconsistencies, and the static site generator is no where near my current standards. So since this is a personal project I have the liberty of throwing it all in the trash and starting over (and learning new things along the way!).

Since my weakest area is front-end (design, Javascript, CSS that doesn't look like it was written by a crazy person, etc.), I decided to jump in there, doing a couple of experiments. I ended up spending the better part of a weekend fiddling with HTML, playing with a couple of CSS frameworks to see what I liked, and incessantly bugging my friend Brian for help. Eventually I got something that I thought looked pretty good and got the 'final' version checked into git.

So you want to know the cool part about git? If you use it right, you have a bunch of commits containing the full history of what you're building! And with a bit of magic you can come up with something like this:

Progress so far

(click on image to see the full size version)

Neat, huh? So how the heck did I manage to pull this off? With some shell scripting wizardry!

Since all of my design is in a single HTML file, index.html, it's easy to comb through the history with the git log command. And to get the commit hashes to iterate over them, just add in some grep, awk, and tac to reverse-sort them (from oldest to newest).

git log -- index.html | grep commit | grep -v initial | awk '{print $2}' | tac

Okay, cool, so now we can flip through the history of our index.html, now how do we make an animated GIF of it? Well, an animation is just a set of images, so we need to figure out how to turn our HTML into an image a bunch of times. This is where wkhtmltopdf comes in handy! The name's kind of a mouthful, but it's a tool that uses WebKit to render HTML and output that to a PDF (or an image). It's super simple to use! Just give it a URL or file name, and then a file to output to, and it does the rest.

wkhtmltoimage --width 1920 --height 1080 index.html index${NUM}.png

Alright, now we've got a bunch of images, how do we string those together into a GIF? For things like this, I always turn to ImageMagick's convert tool, which is the swiss-army-knife of image manipulation. It turns out that if you pass it a bunch of still images and a filename that ends in .gif, it just knows to make a GIF! Incredible! Since we want it to slowly go through the changes so you can play spot-the-difference, we add in a -delay 100 to the command to tell it to wait 100 tens of milliseconds between each frame.

convert -delay 100 index*.png progress.gif

Add in some hackery to remove duplicates (because the rendered page may not change if you change the HTML) and to add a pause of the last frame, and this is what I came up with:

#!/bin/bash

# requires that imagemagick and wkhtmltopdf are installed

mkdir -p progress
git checkout master

mkhtmltoimage --crop-w 1920 --crop-h 1080 https://nickpegg.com progress/0000.png

count=0
commits=$(git log | grep commit | grep -v initial | awk '{print $2}' | tac)

for commit in $(echo $commits | xargs); do
  git checkout "$commit"
  i=$((++count))
  wkhtmltoimage --crop-w 1920 --crop-h 1080 index.html "progress/$(printf "%04d" "$count").png"
done

# Magical one-liner to remove duplicates
md5sum progress/* | \
  sort | \
  awk 'BEGIN{lasthash = ""} $1 == lasthash {print $2} {lasthash = $1}' | \
  xargs rm

# Add an artificial pause by copying the last file a few times
for i in $(seq $((count+1)) $((count+5))); do
  cp progress/$(printf "%04d" "$count").png progress/$(printf "%04d" "$i").png
done

convert -delay 100 progress/*png progress.gif
git checkout master