Skip to content

About

Your year of code, wrapped. A Spotify-Wrapped-style GitHub recap that runs entirely on your own machine.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

GitHub Wrapped

Your year of code, as a full-screen, click-through recap — Spotify Wrapped, but for your commits.

It runs entirely on your machine. You give it a read-only GitHub token, it pulls your own activity, computes the stats locally, and opens an animated slide deck in your browser. The last slide is a 1200×675 card you can download or copy straight into a post.

GitHub Wrapped share card


What you get

Thirteen slides, auto-advancing like Instagram Stories, each one stat:

Slide What it shows
Intro Your avatar, name, and how many days you showed up
Contributions The big number, counting up
Heatmap Your whole year as a glowing contribution grid
Streak Longest consecutive run, with dates
Clock Hour-by-hour histogram and your coding persona (Night Owl, Early Bird, …)
Weekday Your favourite day to build
Languages Top 3 by bytes across everything you touched
Pull requests Opened vs merged, with an animated merge-rate ring
Repos How many you committed to, and the most-starred one
Busiest day Your single heaviest day of the year
Commit The funniest commit message we could find in your history
Percentile A playful, clearly-labelled estimate of where you land
Share The exportable summary card

Slides with nothing to say are skipped automatically — no empty states.


Setup

1. Clone and install

git clone <your-fork-url> github-wrapped
cd github-wrapped
npm install

Requires Node 18 or newer.

2. Create a GitHub token

Go to github.com/settings/personal-access-tokens/new and create a fine-grained token:

  • Repository access: Public repositories (read-only)
  • Expiration: whatever you like — a week is plenty
  • No extra permissions needed

That's enough for every stat except private-repo detail.

Want your private contributions included too?

Fine-grained tokens can't read the aggregate private-contribution data the profile calendar uses. For that you need a classic token with the repo scope, and you need Settings → Profile → "Include private contributions on my profile" turned on.

Without it, private work still shows up in your totals (GitHub reports the count), it just can't be broken down by repo or language.

3. Add it to .env

cp .env.example .env

Open .env and paste the token in:

GITHUB_TOKEN=github_pat_...

.env is gitignored. The token is read only by the local fetch script and is sent to api.github.com and nowhere else — it never reaches the browser, and it's never written into the generated data file.

4. Run it

npm run start

That fetches your data, writes public/data/wrapped.json, and opens the app at http://localhost:5959.


Usage

npm run start                      # fetch + open the app
npm run fetch                      # just refresh the data
npm run fetch -- --year 2024       # a different year
npm run fetch -- --user someone    # someone else's public year
npm run dev                        # open the app with existing data
npm run demo                       # generate fake data to preview the UI

npm run demo needs no token at all — handy for seeing the whole experience before you commit to setting one up.

Getting around

Key Action
→ ↓ j / click right Next slide
← ↑ k / click left Previous slide
Space Pause / resume autoplay
Home / End First / last slide
m Toggle slide sounds (off by default)

Swipe works on touch devices, and holding your finger (or mouse button) down pauses autoplay, like Stories.

Sharing

On the final slide:

  • Download image saves a 2400×1350 PNG (2× of 1200×675, sized for X)
  • Copy to clipboard puts the image straight on your clipboard
  • Share on X opens a pre-filled post — attach the downloaded image to it

If you post yours, we'd love to see it. The card carries a small github-wrapped credit so other people can find their way here.


Configuration

All optional, all in .env:

Variable Default What it does
GITHUB_TOKEN — Required. Your personal access token
GITHUB_USERNAME token owner Whose year to wrap
WRAPPED_YEAR current year Which year to wrap
WRAPPED_TIMEZONE your system zone Used to bucket commits into hours and weekdays

About the timezone: GitHub stamps each commit with the offset it was authored in, so "you're a night owl" is based on your local clock at the time, not UTC. WRAPPED_TIMEZONE is the fallback for commits stamped in UTC — CI jobs and edits made through the GitHub web UI.


How it works

scripts/fetch.ts      CLI: reads .env, runs the pipeline, writes the JSON
src/api/              GraphQL + REST fetching (the only code that sees your token)
  client.ts             auth, retries, rate-limit backoff
  graphql.ts            contribution calendar, commit history, PR search
  rest.ts               token owner, rate limits, language fallback
  errors.ts             every failure, translated into something readable
src/stats/            Pure functions: raw API data in, finished stats out
  streak.ts             longest/current streaks, busiest day, monthly totals
  rhythm.ts             hour + weekday distributions, persona
  languages.ts          bytes by language across repos you touched
  pullRequests.ts       merge rate, merge times
  commits.ts            the funny-commit heuristic
  percentile.ts         the illustrative comparison stat
src/components/       React: one component per slide, plus shared primitives
src/app/              App shell, slide deck definition, navigation, autoplay

The fetch step and the UI are fully decoupled: the script writes a JSON file, the app reads it. Nothing in the browser knows your token exists.

Everything in src/stats/ is a pure function, so the numbers are reproducible and easy to check — npm run demo runs the exact same code over synthetic data.


A note on the percentile slide

The "you're ahead of X% of GitHub" number is an illustrative estimate, not a measurement. GitHub doesn't publish per-user contribution distributions, so it interpolates your total against a hand-set curve that reflects how long-tailed contribution counts are. The reference curve is right there in src/stats/percentile.ts if you want to argue with it.

It's labelled as an estimate on the slide itself. Please don't put it in a performance review.


Troubleshooting

"GitHub rejected your token (401)" — the token is wrong, truncated, or expired. Regenerate it and make sure the whole thing is on one line in .env with no quotes.

"You've hit GitHub's rate limit" — very active accounts need a lot of requests. Wait for the reset time the message prints and run it again. You can also lower the ceiling: npm run fetch -- --max-commits 800.

"A quiet year" — we found almost no public activity. Either your work lives in private repos (see the token note above) or you're looking at the wrong year.

Timing stats look off — check WRAPPED_TIMEZONE, and remember that commits made through the GitHub web UI or by CI are stamped UTC.

The fetch is slow — the commit-history walk is the expensive part, and it's paged per repo. A big year takes a minute or two. It stops at 2500 commits by default; totals stay complete either way, only the timing and commit-message stats are sampled.


Screenshots

Intro Heatmap Share card
docs/intro.png docs/heatmap.png docs/share.png

To capture your own: run npm run demo, open the app, and screenshot the slides you like.


Deploying a public demo (optional)

The app itself can't be usefully hosted — the fetch step is a local Node script, so a deployed build has no way to get anyone's data. What you can deploy is the demo, which gives you a link that shows the full experience without a token.

vercel.json is already set up for this:

{ "buildCommand": "npm run demo && npm run build", "outputDirectory": "dist" }

The npm run demo step matters: public/data/wrapped.json is gitignored, so a build from a fresh clone would otherwise ship a site permanently stuck on the "No data yet" screen. Generating demo data at build time is what makes the deploy work.

On Netlify the equivalent is:

[build]
  command = "npm run demo && npm run build"
  publish = "dist"

The deployed demo shows The Octocat's synthetic year — none of your own data is involved. The Open Graph tags and public/og.png are already wired up, so the link unfurls with the share card when you post it.


Privacy

  • Your token lives in .env, which is gitignored, and is only ever read by scripts/fetch.ts.
  • The only network calls are to api.github.com (data) and Google Fonts (typefaces).
  • public/data/wrapped.json is gitignored — your stats stay on your disk.
  • Nothing is uploaded, logged, or phoned home. There is no server.

License

MIT. Fork it, restyle it, add your own slides.

About

Your year of code, wrapped. A Spotify-Wrapped-style GitHub recap that runs entirely on your own machine.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages