Skip to content

guide

The Graph: Your GitHub contributions, framed in e-ink on the wall — Build Guide

A Raspberry Pi Zero, an e-ink display and one cron job turn the little green squares into silent furniture that updates once a day.

· 12 min read

Devs At Home is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program. We earn from qualifying purchases. This never affects what we recommend or what we say about it.

A Pi Zero with an e-ink HAT pulls your GitHub contribution graph once a day and renders it to a 2.7-inch or 2.13-inch display you can hang or stand on a shelf. The hardware costs less than a dinner out, sips milliwatts and survives power cuts without losing the image. The build takes a weekend if you've never soldered a header.

Key points

  • The Raspberry Pi Zero 2W draws under 200 milliamps at idle with the e-ink HAT attached, so a small USB power supply runs it indefinitely.
  • E-ink holds its image without power after each update, which happens once a day via a cron job that calls the GitHub API with a read-only token.
  • The 2.7-inch E-Ink Display HAT for Raspberry Pi gives you 264×176 pixels; the 2.13inch E-Ink Display HAT V4 Version offers 250×122 at a smaller physical size.
  • You'll solder a 40-pin header to the Pi Zero if the kit doesn't include one pre-attached, then slot the HAT on top and write a Python script to fetch and render the graph.
  • The GitHub token needs only public repository read scope — never grant write or delete permissions for a wall display that sits on your network unattended.
  • Total hardware cost sits in the budget tier; the frame or stand you supply yourself determines whether this looks like a project or a finished piece.

What you are building and why it is worth a weekend

The GitHub contribution graph lives in a browser tab you open once and forget. An e-ink version on the wall turns it into something you glance at without deciding to glance at it. The little green squares sit there, judging silently.

E-ink survives. The image persists without power once drawn. Refresh it once a day and the display pulls no current between updates. Leave it unplugged for a week and it still shows the last graph it rendered.

The Pi Zero 2W is the small one, slightly faster than the original Zero. You'll run a lightweight Python script that calls one API endpoint, parses JSON, draws rectangles into a bitmap and pushes the result to the display driver. That script runs from cron at four in the morning and finishes before you wake.

The parts list: what each item in the kit cart does

Component What it does Notes
RasTech Raspberry Pi Zero 2W Kit with Pin Header Runs the script, fetches the API, pushes pixels to the display Check whether the header is pre-soldered; if not, you'll solder it yourself
2.7-inch E-Ink Display HAT for Raspberry Pi or 2.13inch E-Ink Display HAT V4 Version Renders the graph in black and white, holds the image without power The 2.7-inch version gives you more pixels; the 2.13-inch fits tighter spaces
SANDISK 128GB Extreme microSD Card + Adapter Holds the operating system, the script and logs 128GB is overkill for this build, but faster cards make the OS boot and update less painful

You'll also need a micro-USB power supply — the same cable and adapter that charged phones five years ago. Any reputable phone charger works. The Pi draws so little current that even a cheap supply won't strain.

What you supply yourself, honestly

A frame or a stand. The e-ink HAT sits bare on a desk if you're fine with that, but it reads better in a shallow frame with a white mount or propped in a small acrylic stand. Picture frames sized for 4×6 inch prints work if you cut the mount to expose the display. Office supply shops sell acrylic display stands that hold a board upright without a frame.

A GitHub token with read scope and nothing else. Generate it in your GitHub account settings under Developer Settings, then Personal Access Tokens. Select public repository read permission — the classic token calls it public_repo scope — and nothing more. The token lives in a text file on the Pi that no other service touches. Treat it like a password you never type twice.

Optionally, a soldering iron and solder if the Pi Zero kit arrives without the header pre-attached. A basic temperature-controlled iron and rosin-core solder suffice. You'll solder forty pins in a straight line. It takes twenty minutes the first time.

The build, in numbered phases sized to an evening each

Phase one: prepare the Pi and flash the OS

Download Raspberry Pi Imager from the official Raspberry Pi site. Run it, select Raspberry Pi OS Lite as the image — the version without a desktop, because this Pi never connects to a monitor — and write it to the microSD card. Before you click Write, open the settings gear and configure wireless network credentials and enable SSH with password authentication. This saves you from needing a keyboard and monitor entirely.

Eject the card, slot it into the Pi Zero and power it on. Wait two minutes for it to boot and join your network. Find its IP address in your router's DHCP lease table or use a network scanner. SSH in as the default user and change the password immediately.

Phase two: attach the e-ink HAT and test the display driver

Power off the Pi. If the 40-pin header isn't soldered yet, solder it now — pins facing up through the board, solder joints on the underside. The header needs to sit flush and perpendicular. Hold it in place with tape while you tack opposite corners, then solder the rest.

Slot the e-ink HAT onto the header. The pins align in one orientation only. Press it down until it seats fully. Power the Pi back on and SSH in again.

Enable SPI in raspi-config: run sudo raspi-config, navigate to Interface Options, then SPI, and enable it. Reboot.

Install the vendor's example code. For Waveshare e-ink displays, the Python library lives in their GitHub repository. Clone it, navigate to the example for your specific HAT model — 2.7inch or 2.13inch — and run the test script. You should see a demo pattern appear on the display after a few seconds. If nothing appears, check the HAT is seated properly and SPI is enabled.

Phase three: write the script that fetches and renders your GitHub contributions

Create a Python script that calls the GitHub API, parses the contributions JSON and draws the result to the e-ink display. The API endpoint you want is the GraphQL API at api.github.com/graphql, querying the contributionsCollection field under your user object. Authenticate with the token you generated earlier, passed in the Authorization header as Bearer YOUR_TOKEN_HERE.

Parse the JSON to extract contribution counts by day. For each day, draw a small rectangle on a PIL Image object — the Python Imaging Library handles bitmap manipulation cleanly. Use one of four shades or sizes to represent the contribution count tiers GitHub uses: zero contributions, low, medium, high. Render these rectangles left to right, oldest to newest, wrapping to a new row every seven days to form weeks.

Pass the finished image to the e-ink driver and call its display function. The Waveshare library provides a display() method that takes a PIL Image and pushes it to the screen. Flush the display when done so it enters low-power mode.

Test the script manually. Run it from the command line and watch the graph appear. Refresh it a few times to confirm it pulls live data and doesn't just cache an old image.

Phase four: schedule the daily update and let it run

Add the script to cron so it runs once a day. Open crontab with crontab -e and add a line that runs the script at a time GitHub's rate limits won't notice — early morning works. A line like 0 4 * * * /usr/bin/python3 /home/pi/github_graph.py runs it at 4 AM daily.

Redirect stdout and stderr to a log file so you can diagnose failures without attaching a monitor: 0 4 * * * /usr/bin/python3 /home/pi/github_graph.py >> /home/pi/graph.log 2>&1. Check the log after the first scheduled run to confirm it executed without errors.

Let it run for a week. The image should update every morning. If you unplug the Pi for a day, it'll catch up on the next run — no state persists between updates except the token file.

Where this build actually stalls, and how to get unstuck

The GPIO header soldering goes wrong when you rush it. Heat the pad and the pin together for two seconds, feed solder into the joint — not onto the iron — and remove the iron once the solder flows. A cold joint looks dull and grainy instead of shiny. Reheat it properly rather than adding more solder on top.

The e-ink display shows garbled output or nothing at all when SPI isn't enabled or the HAT isn't fully seated. Check the physical connection first — press the HAT down firmly and confirm every pin entered its socket. Then verify lsmod | grep spi shows the SPI kernel modules loaded. If not, revisit raspi-config and reboot.

The GitHub API returns a 401 Unauthorized when the token is malformed or lacks the correct scope. Generate a new token, confirm you selected the public_repo scope or equivalent read permission, and paste it exactly — no extra whitespace or line breaks. Test it with curl before you blame the script: curl -H "Authorization: Bearer YOUR_TOKEN" https://api.github.com/user should return your user object.

The cron job runs but the display doesn't update. Check the log file for Python exceptions. A common failure is the script running as root instead of your user, breaking file paths. Another is missing the DISPLAY and XAUTHORITY environment variables, though you won't need those without a desktop environment. The real issue is usually an unhandled exception — reading the log tells you which library import or API call failed.

Safety, etiquette and the cleanup that keeps you allowed to do this again

The Pi Zero runs cool enough to leave unattended, but don't bury it under papers or inside a closed box. Passive airflow suffices; no heatsink required. If you mount it inside a frame, leave gaps at the top and bottom so warm air escapes.

The GitHub token sits in plaintext on the microSD card. Anyone with physical access to the Pi can read it. Don't give this token write or delete permissions, and don't use the same token anywhere else. If the Pi is stolen or the card is lost, revoke the token in your GitHub settings.

Respect GitHub's rate limits. One API call per day barely registers. Don't poll every minute or scrape historical data in a loop — the GraphQL API has a points system and you'll burn through it fast. If you do hit a rate limit, the API returns a clear error. Wait an hour and try again.

Keep the Pi's operating system updated. Run sudo apt update && sudo apt upgrade once a month. The Pi connects to your home network with the same credentials as your other devices. An unpatched SSH service is how botnets grow.

Ways to take it further once the base build works

Add a second metric to the display. Fetch pull request counts, issue activity or commit streaks from the same API call and render them as a bar graph below the main grid. The e-ink HAT has enough pixels for two small charts.

Switch the display to show someone else's contributions. Pass a username as a command-line argument and render their graph instead of yours. The script logic stays identical; only the query changes. Rotate between two developers every day by running two cron jobs with different arguments on alternating days.

Trigger an update on demand with a physical button. Wire a GPIO button to the Pi and attach a script that runs the display update when pressed. You'll need a pull-up resistor and a debounce delay in the button handler. The Waveshare wiki has GPIO pinout diagrams.

Log the contribution counts to a file over time and graph the trend. Store daily totals in a CSV and render a line chart showing whether your activity is increasing or declining. Overlay it on the main graph or display it on a second e-ink HAT.

Port the script to another data source. The same Pi and HAT combination can display weather, stock prices, build status or calendar events. The e-ink refresh cycle and power draw stay low regardless of what you render.

Common questions

Can I use a Raspberry Pi 4 instead of a Zero 2W?

Yes, but it's overkill and draws more power. The Pi 4 pulls several watts at idle compared to the Zero 2W's fraction of a watt. The GPIO header layout is identical, so the e-ink HAT fits either board. Use a Pi 4 if you already own one and don't want to buy a Zero, but the Zero is the correct choice for a project that runs one cron job per day.

Does the e-ink display need to refresh every day if my contributions haven't changed?

No, but there's little reason not to. The script completes in under ten seconds and the display draws negligible power during refresh. If you want to skip updates when nothing changed, hash the contribution data and compare it to the previous day's hash before rendering. Store the hash in a file and read it at the start of each run.

Will this work with a private repository contribution graph?

Yes, if you generate a token with private repository read scope. The API endpoint is the same; GitHub returns private contributions when the token has permission. Be aware that the display shows your full contribution count without filtering, so anyone who sees the frame will know how active you've been even if they can't see the repositories themselves.

How long does the e-ink display last before the image degrades?

E-ink pixels degrade slowly after hundreds of thousands of refresh cycles. A daily update gives you several decades of life. The backplane wears faster if you refresh rapidly or leave the display in direct sunlight, but neither applies here. The display will outlive the Pi.

Can I run this off battery instead of mains power?

Yes, with a USB battery bank. The Pi Zero 2W sips so little power that a ten-thousand milliamp-hour bank runs it for days. The challenge is charging — the bank's charge controller might shut off when the Pi's current draw falls below its minimum threshold. Look for a bank that supports low-current devices or pass-through charging.

What happens if my internet connection drops?

The script fails and logs an error. The display holds the previous day's graph because e-ink retains its image without power. The next day's cron job will try again. If you want the script to retry on failure, wrap the API call in a loop with exponential backoff. Three retries spaced five minutes apart catch transient network issues without hammering the API.

Does the GitHub API charge for these requests?

No. The GraphQL API is free for authenticated requests within the rate limit. One query per day sits well below the threshold. The API uses a points system where each query costs points based on its complexity, and you get five thousand points per hour. A simple contribution query costs a handful of points.

Can I display multiple users' graphs side by side?

Yes, but you'll need a larger e-ink display or multiple HATs. The 2.7-inch and 2.13-inch displays are too small to fit two full grids legibly. Waveshare makes larger e-ink panels up to 7.5 inches that attach via SPI the same way. Run two API queries in the same script and render the results as adjacent grids on one large display.

Who should build this and who should skip it

Build this if you've soldered before or are willing to solder forty pins in a straight line without panicking when the first few look ugly. The rest is software. If you're comfortable editing Python and SSH-ing into a Pi, the build takes a weekend and works indefinitely after that.

Skip it if you want a polished product out of the box. This is a project, not an appliance. You'll troubleshoot cron, wrestle with SPI drivers and hand-tune rectangle sizes in the rendering code. The result is a wall decoration that silently judges your commit history and survives power cuts. The GitHub graph becomes furniture, and you glance at it without deciding to.

One email a week

New reviews, teardowns and the occasional deal worth knowing about. No spam, unsubscribe any time.