Skip to content

Repository files navigation

ghpulse

Your GitHub activity, visualized as generative SVG art.

Every visualization is a file you can edit. No two developers produce the same output.


What it does

Most GitHub stats tools give you a fixed set of cards and let you recolor them. ghpulse has no fixed visualizations at all. What it draws is described by a scene: a TOML document declaring a canvas, a palette and a list of layers that bind your data to geometry.

The seven scenes below ship with it. They are ordinary scene files using nothing you cannot use yourself, so the way to get something new is to copy one and edit it.

See the scene format reference to write your own.

Scenes

Every scene has a dark and a light palette in one file. Pass --light for the light variant, and pair the two with GitHub's #gh-dark-mode-only and #gh-light-mode-only URL fragments to switch automatically.

nebula (default)

Your repositories as a star field. Each language gets a sector, each repository sits in its language's sector at a radius from its commit rank, and repositories sharing a language are joined by constellation lines. Position comes from your data, so two developers with the same repository count still look different.

heatmap

Your real daily contribution calendar for the trailing year, correctly dated, with per weekday totals, current and longest streaks.

terminal

Retro terminal. Monospace stats and a language breakdown, typed out as if you were reading them off a green screen.

radar

A spider chart over your language mix, scaled by rank so a typical distribution has a distinct shape rather than collapsing into a circle.

fingerprint

A mirrored waveform with one bar per repository, height from commit rank and color from language. The pattern is a function of your repositories.

synthwave

Retrowave sunset: striped neon sun, a perspective grid receding to the vanishing point, and language pillars rising from the horizon.

prism

White light enters a glass prism and refracts into a fan of beams, one per language, each sized by its share.

Usage

CLI

# Default scene
ghpulse --token "$ACCESS_TOKEN"

# Pick a scene, and its light palette
ghpulse --token "$ACCESS_TOKEN" --scene terminal
ghpulse --token "$ACCESS_TOKEN" --scene terminal --light

# Your own scene
ghpulse --token "$ACCESS_TOKEN" --scene ./my-scene.toml

# Any size, any title
ghpulse --token "$ACCESS_TOKEN" --width 1200 --height 500 --title "2026"

# Save raw data, then re-render offline as often as you like
ghpulse --token "$ACCESS_TOKEN" --dump-json stats.json
ghpulse --from-json stats.json --scene radar --output ./out

# PNG export (requires --features png)
ghpulse --token "$ACCESS_TOKEN" --format both

All options

ghpulse [OPTIONS]

  -t, --token <TOKEN>              GitHub personal access token [env: ACCESS_TOKEN]
      --scene <SCENE>              Scene name or path to a scene file [default: nebula]
      --light                      Render the light palette
      --format <FORMAT>            Output format: svg, png, both [default: svg]
  -o, --output <DIR>               Output directory [default: .]
      --width <N>                  Canvas width, overriding the scene
      --height <N>                 Canvas height, overriding the scene
      --title <TEXT>               Title text, overriding the scene
      --rows <N>                   Cap how many rows any layer draws
      --config <FILE>              Config file [default: ghpulse.toml]
      --exclude-repos <PATTERNS>   Comma-separated glob patterns
      --exclude-langs <LANGS>      Comma-separated language names
      --exclude-private            Skip private repos
      --exclude-archived           Skip archived repos
      --min-stars <N>              Minimum stars to include a repo
      --min-commits <N>            Minimum commits to include a repo
      --dump-json <FILE>           Save raw stats to JSON
      --from-json <FILE>           Render from cached JSON (no API calls)
      --list-scenes                List available scenes
      --list-langs                 List detected languages
      --scene-dir <DIR>            Load custom scenes from a directory
      --max-retries <N>            Max retries for flaky API endpoints [default: 10]
      --verbose                    Verbose output
      --debug                      Debug output
  -V, --version                    Print version

Config file

Anything settable on the command line is settable in ghpulse.toml. A flag always wins over the file.

scene = "heatmap"
width = 900
title = "My year in code"
exclude-langs = "HTML,CSS"

GitHub Action

Add this to your profile README repo:

# .github/workflows/ghpulse.yml
name: Generate Stats

on:
  schedule:
    - cron: "0 0 * * *"
  workflow_dispatch:

permissions:
  contents: write

jobs:
  generate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: QaidVoid/ghpulse@v1
        with:
          token: ${{ secrets.ACCESS_TOKEN }}
          scene: nebula
          exclude-repos: "test-*"

      - name: Commit
        run: |
          git config user.name "ghpulse[bot]"
          git config user.email "ghpulse[bot]@users.noreply.github.com"
          git add .
          git commit -m "chore: update ghpulse stats" || true
          git push

Then in your README:

![My GitHub Universe](./nebula.svg#gh-dark-mode-only)
![My GitHub Universe](./nebula-light.svg#gh-light-mode-only)

Writing a scene

A scene binds a data source to a mark, with every visual property set from a constant or computed from your data:

[[layer]]
data   = "repos"
mark   = "circle"
layout = "radial"
region = { y = 0.15, height = 0.7, inset = 20 }

[layer.encode]
x    = "{slot.centre_x + cos(slot.angle) * slot.radius * commits.rank}"
y    = "{slot.centre_y + sin(slot.angle) * slot.radius * commits.rank}"
r    = "{lerp(2, 12, commits.norm)}"
fill = "{contrast(language.color, palette.background)}"

Point your editor at schema/scene.schema.json for completion on mark, channel, scale and layout names while you type. Then:

ghpulse --from-json stats.json --scene ./my-scene.toml

The full reference is in docs/scenes.md.

Token permissions

Repos are discovered through your commit contributions, so anything you have actually committed to (org repos, forks, OSS projects) surfaces regardless of token type. Repos you own but never committed to will not appear.

Classic PAT (recommended, simpler)

Scope What it unlocks
read:user Profile, contribution data, language breakdown
read:org Org repos and org-admin detection
repo Private repos and traffic/view counts

Minimum for public data: read:user. Add read:org for org-admin attribution and repo for private repos and traffic.

Fine-grained PAT (more restrictive)

Repository permissions

  • Metadata: Read (required)
  • Contents: Read
  • Administration: Read (only for traffic/view counts)

For org repos, the org owner must:

  1. Enable fine-grained tokens in Org Settings, Personal access tokens, Allow access via fine-grained personal access tokens.
  2. Approve your token if approval is required.

When creating the token, set Resource owner to each org you want included (one token per resource owner). If your org does not allow fine-grained tokens, use a classic PAT instead.

Build from source

git clone https://github.com/QaidVoid/ghpulse.git
cd ghpulse
cargo build --release

# with PNG support
cargo build --release --features png

License

MIT OR Apache-2.0

About

Your GitHub activity, visualized as generative SVG art

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages