A star-history, but for commits.
Watch any GitHub user's commits stack up across their whole lifetime — as one satisfying, rising chart.
Type a username, get their entire coding career as a curve. It's a little hypnotic, it's a great flex, and it drops straight into your README.
👉 commit-history.com/torvalds · /gaearon · /sindresorhus
- 📈 Lifetime commit curve — every public commit since the account was born, accumulated month by month. The same data as the green contribution graph, not the noisy issues/PRs calendar.
- ⚔️ Compare anyone — throw in comma-separated names (
/torvalds,gaearon,antfu) and race their trajectories on one chart. Flip to Aligned mode to line everyone up at "month zero" regardless of when they joined. - 🏆 Leaderboard — an all-time ranking of everyone who's been looked up, sortable by public commits, private contributions, total activity, or followers.
- 🔒 Public & private — for users who expose private contributions, see the hidden half of their activity too (kept separate, never silently summed).
- 🖼️ Embed it anywhere — a live SVG chart for your own README (see below).
- ✏️ Hand-drawn charm — an xkcd-style sketch aesthetic, a deliberate homage to the original.
Drop a live, auto-updating chart into your profile page or any markdown file. This snippet centers the chart, follows the viewer's GitHub theme (light/dark), and links back to your full history:
<div align="center">
<a href="https://commit-history.com/YOUR_USERNAME">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://commit-history.com/embed/YOUR_USERNAME?theme=dark" />
<img alt="YOUR_USERNAME's commit history" src="https://commit-history.com/embed/YOUR_USERNAME" />
</picture>
</a>
</div>Here's Linus Torvalds:
pnpm install
cp .env.example .env # add your token (and optionally a database URL)
pnpm dev # → http://localhost:3000# .env
GITHUB_TOKEN=<classic PAT with the read:user scope> # required
DATABASE_URL=<neon postgres url> # optional — see caching belowA classic Personal Access Token with read:user is enough for the public commits of any user.
- Data comes from GitHub's GraphQL API (
user.contributionsCollection.totalCommitContributions). A window spans at most a year, so a lifetime is sliced into monthly windows and fetched in batched, parallel queries (src/lib/github.ts) — one big query per month trips GitHub's resource limits on older accounts. - All fetching happens in server functions (
src/lib/commit-history.ts), so the token never reaches the browser. - The chart is hand-rolled inline SVG (
src/components/CommitChart.tsx) — no chart library — which is what makes the xkcd filter and the standalone embed (src/lib/chart-svg.ts) possible. - Caching (
src/lib/cache.ts) is incremental: past months are immutable, so a returning user only re-fetches the trailing month. WithDATABASE_URLset it persists to Neon Postgres (via Drizzle) and powers the leaderboard + recent lookups; without it, it falls back to an in-memory cache so the app still runs.
Some accounts game the board (botted commits, bought followers). To hide one until you've
investigated, suspend it — a soft, reversible flag (entities.suspended_at). Suspended
accounts drop off the leaderboard and "recently looked up" but stay directly viewable with an
under-review notice. Run with bun (it auto-loads your local .env):
bun run suspend <login> "botted commits" # suspend (asks to confirm)
bun run suspend --remove <login> # reactivate
bun run suspend --list # list suspended accountsProfile metadata (followers, repos, bio, …) is fetched on lookup and cached. To re-pull it from
GitHub manually — e.g. someone's follower count is stale — refresh it. Needs GITHUB_TOKEN and
DATABASE_URL in .env; run with bun (it auto-loads your local .env):
bun run refresh <login> # refresh one account
bun run refresh # refresh only un-backfilled rows (followers is null)
bun run refresh --all # refresh every userThe build emits a standalone Node server via nitro — pnpm build && pnpm start serves the whole app on port 3000. The multi-stage Dockerfile packages exactly that, so any Docker host works; production runs on Coolify (build pack: Dockerfile, port 3000) behind Cloudflare, which edge-caches /embed/* — the embeds already send s-maxage for any CDN.
| Setting | Value |
|---|---|
| Build | docker build . (or pnpm build for bare Node) |
| Run | container CMD / pnpm start → listens on :3000 (PORT overridable) |
| Environment variables | GITHUB_TOKEN (required), DATABASE_URL (for the persistent cache + leaderboard) |
TanStack Start + React 19 · TanStack Query · Tailwind v4 · Drizzle + Neon Postgres · Biome.
A loving homage to star-history.com.