Skip to content
HarrisJDCampbellPublic

About

Your GitHub contribution graph, played as a basketball shootaround. A GitHub Action for your profile README.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

17 Commits

Folders and files

Repository files navigation

hoops

Your GitHub contribution graph, played as a basketball shootaround.

A little pixel player walks along under your contribution grid. For every day you contributed, he reaches up, the day's square floats down into his hands and becomes the ball, and he dribbles, steps in and shoots. Every shot goes in, worth that day's contribution count. When the year is done he jogs to the hoop and dunks.

A pixel basketball player shooting a year of GitHub contributions, one made shot per day

That's the maintainer's real last year, regenerated every night by this repo's own workflow.

Put it on your profile

Your profile README lives in a repo named after your username (you/you). Add this workflow to it as .github/workflows/hoops.yml:

name: hoops

on:
  schedule:
    - cron: "0 0 * * *" # every night
  workflow_dispatch:

permissions:
  contents: write

jobs:
  generate:
    runs-on: ubuntu-latest
    steps:
      - uses: HarrisJDCampbell/hoops@v1
        with:
          github_user_name: ${{ github.repository_owner }}
          out_dir: dist

      - uses: crazy-max/ghaction-github-pages@v3
        with:
          target_branch: output
          build_dir: dist
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Run it once from the Actions tab, then add this to your README, replacing you with your username:

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/you/you/output/hoops-dark.svg" />
  <img width="100%" alt="my contributions, played as a basketball shootaround" src="https://raw.githubusercontent.com/you/you/output/hoops.svg" />
</picture>

Options

Input Default What it does
github_user_name repo owner Whose calendar to play.
github_token github.token Reads the public contribution calendar. The default workflow token is enough.
out_dir dist Where hoops.svg (light) and hoops-dark.svg (dark) are written.
jersey blue-gold A preset: blue-gold, purple-gold, green-white, red-white, orange-navy, black-teal, slate-lime, burgundy-sand, teal-coral, magenta-cyan, teal-orange, purple-mint, green-gold, wine-sky, cobalt-orange. Or your own "primary,trim" hex pair, e.g. "#1d428a,#ffc72c".
skin #c68b59 The player's skin tone, as a hex colour.
hair #2b1d14 The player's hair colour, as a hex colour.
hair_style short The player's hairstyle: short, long, or headband.
pace 1 How relaxed he plays, from 0.25 (quick) to 4 (very slow). At 1 a shot takes about 3 seconds, so an active year runs several minutes.
max_seconds unset Positive maximum loop duration in seconds. If the requested pace would exceed it, pace is lowered just enough to fit. If even pace 0.25 is too long, it renders at 0.25 and logs a warning.
order oldest-first oldest-first (starts at the beginning of the year) or newest-first (starts at the most recent week beside the hoop and walks backwards).
footer contribution summary Custom text for the bottom line, or none to hide it. Long text is scaled to fit the SVG width.

Outputs: light and dark, the paths of the two files.

Run it locally

No dependencies, just Node 20 or newer.

git clone https://github.com/HarrisJDCampbell/hoops && cd hoops
GITHUB_TOKEN=$(gh auth token) node bin/hoops.mjs --user octocat --jersey purple-gold --out dist
open dist/hoops-dark.svg

--fixture test/fixtures/calendar.json renders a made-up year without touching the network.

Optional GIF and MP4 export

The local CLI can capture one complete animation loop as a GIF or MP4. This optional path needs Node 20+, Playwright with its Chromium browser, and FFmpeg with GIF and libx264 encoders. The GitHub Action does not load or install these tools and remains dependency-free.

npm install --no-save --package-lock=false playwright
npx playwright install chromium
brew install ffmpeg

node bin/hoops.mjs --fixture test/fixtures/calendar.json --export gif --out dist/hoops.gif --fps 10 --width 900 --theme dark
node bin/hoops.mjs --fixture test/fixtures/calendar.json --export mp4 --out dist/hoops.mp4 --fps 10 --width 720 --theme light

--out is a file path when exporting and must end in .gif or .mp4. --fps is an integer from 1 to 30 (default 10); --width is an even number from 320 to 1800 (default 900), and the height follows the SVG aspect ratio. --theme is dark or light (default dark). The export uses the full buildTimeline() duration, rounding up to the next frame; GIF timing is quantized to centiseconds. GIFs keep a transparent background and loop continuously; MP4s use the selected theme background and play one loop. Frames are streamed to FFmpeg, so the CLI does not create a directory of temporary images.

Install FFmpeg from your platform's package manager and ensure ffmpeg is on PATH. The exporter reports missing Playwright, Chromium, or required FFmpeg encoders before completing an output.

How it works

  • One clock for everything. buildTimeline() lays out the whole game as timed events: walk, grab, dribble, release, swish, score. render() then compiles those events into one CSS @keyframes rule per element on a single shared loop length, so the ball, the player, the net, the falling squares and the scoreboard can't drift out of sync.
  • Real shot arcs. Each shot is a quadratic Bézier with its control point halfway across, so the ball moves sideways at a constant speed while its height follows a parabola, like a real shot under gravity. The curve is sampled into short straight segments because animating along a path directly is unreliable in Safari.
  • Plain SVG and CSS. No JavaScript runs in the image, so it animates anywhere GitHub shows an <img>.
  • What counts. The numbers come from the same calendar your profile shows: commits, pull requests, issues and reviews. If you've turned on "include private contributions", private work counts too, as anonymous numbers.

Contributing

Ideas and pull requests are welcome. Check the open issues, especially the ones labelled good first issue. CONTRIBUTING.md covers setup, where things live, and how to check an animation by eye.

Credits

Inspired by Platane/snk, the contribution snake. MIT licensed.

About

Your GitHub contribution graph, played as a basketball shootaround. A GitHub Action for your profile README.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages