How to recreate GitHub’s contribution graph

Written by vinnyoodles | Published 2017/01/07
Tech Story Tags: d3 | javascript | data-visualization | reddit

TLDRvia the TL;DR App

I’m sure most of the people that read this know of GitHub and Reddit. As an avid user of both, I wanted to recreate GitHub’s heat map/contribution graph using Reddit’s data to both learn about its implementation as well as see my Reddit activity in a neat visualization.

I used d3.js, javascript library for building graphics, jQuery, super handy for DOM manipulation, and Bootstrap, great for developers with poor design skills.

First, I’ll discuss the UI component and how d3 was used in this. I had a lot of help with the UI from this example. For GitHub’s use case and this use case, the graph is always a year’s length which makes it simpler because the graph will always be the same length.

The entire graph is a SVG element which contains sub-elements which are also SVGs. Each square which represents a day will contain an id tag in the format of %Y-%m-%d so something like 2017-01-06. The squares are first rendered with the same color, #eee.

The square’s position is determined by its day and week. The x position is determined by the week number, %U.

The week 1 of YYYY starts with a Sunday (according to %U). The days in the year before the first week are in week 0.

The y position is determined by the day of the week. Sundays are first and Saturdays are last, it doesn’t make a difference if it is 0-indexed or 1-indexed. This would look something like

d3.select('.js-heatmap').enter().append('svg').enter().append('rect').attr('width', CELL_SIZE).attr('height', CELL_SIZE).attr('x', (d) => d3.timeFormat('%U')(d) * CELL_SIZE).attr('y', (d) => d.getDay() * CELL_SIZE)

For the square/grid coloring, each grid needs a ranking or score to determine its color. At this point, let’s assume each grid already has a score. (I’ll discuss how I calculated the score with Reddit’s API below this).

Determining a grid’s color depends on a few factors: number of colors, grid ranking and ranking extremes (min and max). Basically, the score will be projected onto a scale based on the number of colors. In this case, I’m using 7 colors so the score with the highest ranking will receive color #7, the ranking with a score near the mean has color #3 and so on. Using d3’s API, this would look like.

d3.scaleQuantize().domain([MIN, MAX]).range(d3.range(NUMBER_OF_COLORS).map((d) => `color${d}`)); // Assign the grid the css class.

I categorized the colors into a certain css class like the following.

.color0 { fill: #d6e685 }.color1 { fill: #44a340 }.color2 { fill: #28752d }.color3 { fill: #1e6823 }.color4 { fill: #365e2f }.color5 { fill: #144e12 }

Onto how I used Reddit’s API to pull data on a specific user. I used Reddit’s different types of contribution to calculate the total amount of activity in a day. For example, the number of submitted posts, comments, upvotes and downvotes in a day translates to its total score. Thankfully, the endpoints for these objects are public so when a user enters a username. Therefore, I can easily make a GET request or multiple to pull the data. Once all the data is fetched, it is then used to render the heat map.

The source code for this is at https://github.com/vinnyoodles/reddit-heatmap and the tool is live at https://vinnyoodles.github.io/reddit-heatmap/.

I’m a Computer Science student at Virginia Tech that loves to write code and lift weights 💪. LinkedIn Github


Published by HackerNoon on 2017/01/07