npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

contribution-graph-3d

v0.1.1

Published

A dynamic, drag-to-orbit 3D WebGL skyline render of a GitHub contribution graph, with a GitHub-style 2D grid and a framework-agnostic data fetcher.

Readme

contribution-graph-3d

A drag-to-orbit 3D WebGL skyline render of a GitHub contribution graph, built on three.js. Ships with the classic GitHub-style 2D grid (that swaps to 3D on click) and a framework-agnostic helper for fetching a user's contribution calendar from GitHub.

3D skyline render of a GitHub contribution graph

It grows out of a flat GitHub-style grid, which is what renders first:

2D GitHub-style contribution grid

See it running on my site at strydr.net (click the grid on desktop to enter 3D).

  • 3D skyline: every active day is a pillar whose height and color track its contribution count. Animated entrance, on-demand rendering, drag to orbit.
  • 2D grid: GitHub-style weekday × week layout on desktop, a transposed month-by-day layout on mobile. No CSS framework required.
  • Data fetcher: a tiny, dependency-free function over GitHub's GraphQL API.
  • Themeable: the whole 5-step color ramp and proportions are props.
  • three is dynamically imported, so it never touches your initial bundle.

Install

npm install contribution-graph-3d three react

react (>=18) and three (>=0.150) are peer dependencies, so bring your own.

Quick start

1. Fetch the data (server-side)

fetchContributions needs a GitHub token, so it must run on a server. Here's a Next.js App Router route handler:

The fetcher lives on the contribution-graph-3d/server subpath, kept separate from the React components so importing it into a server route never pulls a 'use client' module into your server bundle.

// app/api/contributions/route.ts
import { NextResponse } from 'next/server'
import { fetchContributions } from 'contribution-graph-3d/server'

export const dynamic = 'force-dynamic'

export async function GET() {
  const data = await fetchContributions({
    login: 'strvdr',
    token: process.env.GITHUB_TOKEN!,
  })
  return NextResponse.json(data)
}

The token only needs read:user scope. A classic PAT or a fine-grained token both work. Never expose it to the browser.

2. Render the widget

'use client'
import { ContributionGraph } from 'contribution-graph-3d'

export function Profile() {
  // Fetches /api/contributions on mount; click the desktop grid to go 3D.
  return <ContributionGraph />
}

That's it. The flat grid renders immediately and swaps to the 3D skyline when clicked (Esc to exit).

Usage patterns

Bring your own data

Skip the internal fetch by passing data directly (server component → client):

import { ContributionGraph } from 'contribution-graph-3d'
import { fetchContributions } from 'contribution-graph-3d/server'

const data = await fetchContributions({ login: 'strvdr', token })
return <ContributionGraph data={data} />

Just the 3D renderer

If you only want the skyline, use ContributionGraph3D with the weeks array:

import { ContributionGraph3D } from 'contribution-graph-3d'

<ContributionGraph3D weeks={data.weeks} height={400} />

Custom endpoint

<ContributionGraph fetchUrl="/my/contributions.json" />

Theming

Every color and proportion has a default (the "Tokyo Night" palette). Override any subset via the theme prop; unset fields fall back to the defaults:

<ContributionGraph
  theme={{
    // Five-step intensity ramp: [empty, low, …, high]
    levels: ['#161b22', '#0e4429', '#006d32', '#26a641', '#39d353'],
    heightScale: 2.2, // taller skyline
    labelColor: '#8b949e',
  }}
/>

| Field | Applies to | Default | Meaning | | ------------------- | ---------- | ------------------------ | ----------------------------------------- | | levels | 2D + 3D | Tokyo Night green ramp | 5-step ramp, index 0 = empty, 4 = most | | labelColor | 3D | #7982a9 | Axis-label text | | labelOpacity | 3D | 0.95 | Axis-label opacity when faded in | | heightScale | 3D | 1.9 | Vertical exaggeration of the skyline | | gridLineColor | 3D | #363b54 | Empty-day outlines on the ground plane | | shadowOpacity | 3D | 0.28 | Drop-shadow strength | | monthLabelColor | 2D | #414868 | Month axis labels | | weekdayLabelColor | 2D | #414868 | Weekday axis labels | | captionColor | 2D | #414868 | Legend + caption text | | cellOutline | 2D | rgba(65,72,104,0.15) | Per-cell outline (any CSS color) | | promptSymbolColor | 2D | #7aa2f7 | ❯ prompt glyph | | promptTextColor | 2D | #c0caf5 | git log … header text | | countTextColor | 2D | #565f89 | "N contributions in the last year" text |

defaultTheme and resolveTheme(partial) are exported if you want to inspect or compose themes yourself.

API

<ContributionGraph />

The full widget: 2D grid, responsive fallback, and click-to-3D.

| Prop | Type | Default | Notes | | ---------- | --------------------------- | ---------------------- | ------------------------------------------------ | | data | Contributions | none | Pre-fetched calendar. If omitted, fetches. | | fetchUrl | string | /api/contributions | JSON endpoint, used only when data is unset. | | theme | Partial<ContributionTheme>| default palette | Color / proportion overrides. | | enable3D | boolean | true | Allow click-to-3D on desktop. | | height | number | 340 | 3D canvas height in px. |

<ContributionGraph3D />

The standalone skyline.

| Prop | Type | Default | Notes | | --------- | ---------------------------- | ------- | ---------------------------------- | | weeks | Week[] | (req.) | Required. One entry per week. | | theme | Partial<ContributionTheme> | default | Color / proportion overrides. | | height | number | 340 | Canvas height in px. | | onReady | () => void | none | Fired when the entrance completes. |

fetchContributions(options)

Imported from contribution-graph-3d/server. Returns Promise<Contributions>. Server-side only.

| Option | Type | Default | Notes | | ----------- | ------------------- | ----------------------- | ---------------------------------------- | | login | string | (req.) | Required. GitHub handle. | | token | string | (req.) | Required. Token with read:user scope. | | userAgent | string | contribution-graph-3d | Sent as User-Agent. | | fetch | typeof fetch | global fetch | Inject a custom fetch. |

Types

interface Day { date: string; count: number; weekday: number }
type Week = Day[]
interface Contributions { totalContributions: number; weeks: Week[] }

date is YYYY-MM-DD; weekday is 0 (Sun) to 6 (Sat).

Accessibility & performance

  • Honors prefers-reduced-motion: skips the entrance animation and renders the resting skyline directly.
  • The 3D canvas renders on demand (one frame per drag event, not a continuous render loop), so it costs nothing while idle.
  • three loads via dynamic import(), keeping it out of your initial JS bundle.

Development

npm install
npm run build      # bundle to dist/ (ESM + CJS + types) via tsup
npm run typecheck  # tsc --noEmit

License

MIT © Strydr Silverberg