Writing on your Github Contributions Heatmap

Writing on your Github Contributions Heatmap

I've thought for a while now that the Github contributions heatmap is a particularly uninteresting page. This thought popped into my head once more after reading a fantastic ebook on the fundamentals of Git, so armed with new confidence and knowledge of Git commits, I decided to fake contributions on my profile in order to spell out words on my heatmap.

I knew that the contributions chart responded to commits in a Git repo, along with issues, pull requests, and other Github specific interactions. The commits seemed to be the easiest to fake (although I am a big fan of the relatively new Github CLI), so I found where dates are considered in a commit object and tried faking them to see if Github would accept them. The only two places I could see were the author date and the commit date. Faking the committer date was a simple matter of prepending the git commit command with an env variable GIT_COMMITTER_DATE. Changing the author date was even easier as git commit accepts a --date argument! So making a commit for a specific date came up to be:

$ LC_ALL=C GIT_COMMITTER_DATE="$(date --date='01/01/1970 12:00')" git commit -a -m"01/01/1970 12:00" --no-edit --date "$(date --date='01/01/1970 12:00')"

Running this will give you a commit from 01/01/1970 which unsurprisingly is the earliest date you can have a commit on. We're not stopping there though, even though it is funny how the UI is broken on your profile page after doing that.

Now with a basis to build on, I decided to package this up into a simple Python script, and add some helper methods around it. Namely, being able to "draw" in a particular (x, y) position on the heatmap.

def get_origin_datetime(year):
    Returns the datetime of (0,0) on the contributions heatmap.

    This is horrific code.
    d = datetime.datetime(year, 1, 1, 12)

    while d.weekday() != 6:
        d += datetime.timedelta(1)

    return d

def xy_to_datetime(year, x, y):
    return get_origin_datetime(year) + datetime.timedelta((7 * x) + y)

Then I used a handy Dot Matrix Tool to create a font which was 3x5 pixels, and translated each character into a simple template:

    's': [
        (0, 0), (1, 0), (2, 0),
        (0, 1),
        (0, 2), (1, 2), (2, 2),
                        (2, 3),
        (0, 4), (1, 4), (2, 4),

With this done, there was only one method left to implement and we were good to go:

def draw_letter(year, letter, letter_number):
    The letter_number is to calculate the offset to apply in the grid.
    x_offset = letter_number * (LETTER_WIDTH + INTER_LETTER_SPACE_WIDTH)
    # This centres the letter vertically
    y_offset = 1

    for coord in LETTERS[letter]:
        commit_on_xy(year, x_offset + coord[0], y_offset + coord[1])

To view this more sensibly, check out this gist. The only improvement I'm thinking about at the minute is to include an opacity/darkness option on each pixel that is drawn to add a bit more detail to the lettering. But for a few hours on a Saturday morning, I'm happy with the result :)

Show Comments