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

git-hash-art

v0.16.1

Published

<div align="center">

Readme

git-hash-art

Deterministic generative art from git commit hashes.

Every commit becomes a one-of-a-kind abstract composition — same hash, same image, forever.

npm version npm downloads license types gallery

Generated from the latest commit hashes of well-known open-source repos:

| react | svelte | deno | zod | | :---: | :---: | :---: | :---: | | tailwindcss | bun | three.js | htmx |

Browse the full gallery →


How it works

A commit hash seeds a deterministic PRNG that drives every decision in a multi-stage rendering pipeline:

  • 17 visual archetypes — fundamentally different personalities (minimal-spacious, neon-glow, watercolor-wash, shattered-glass, celestial, …), with ~15% of hashes blending two
  • 34 active shapes across 5 categories — basic, complex, sacred geometry, procedural, and noise-field organics whose silhouettes are contoured from hash-seeded simplex noise, so no two renders ever repeat a form
  • 17 render styles — watercolor bleeds, ink-bleed with fiber wicking and satellite droplets, gravity-correct paint drips, stipple, cross-hatch, hand-drawn wobble, and more
  • Attractor substrates — around a third of images underlay a hash-seeded strange attractor: a quarter-million orbit iterations accumulated into a density field, giving filigree far finer than any shape vocabulary can build, with the shape layers settling along its spines
  • Generative mark-making — branching growth structures (coral roots or crack networks), sweeping hero flow ribbons, and rare hard-mirror Rorschach symmetry
  • Composition & color discipline — rule-of-thirds anchors with edge bleed, dominant/secondary/accent color hierarchies, background-aware blend modes, value hierarchy by scale, and a scale hierarchy that guarantees every image a large/medium/small reading
  • Color zoning — a low-frequency field splits the canvas into two color passages so large masses carry genuinely different hues, backed by a protected accent quota and a synthesized split-complementary accent for palettes that would otherwise collapse to one hue
  • Alpha-aware contrast — shape colors are pushed away from the ground by enough to survive the transparency they're painted at, so soft archetypes stay soft without going invisible
  • Physical finishing — torn-paper deckle frames, translucent vellum sheets, film grain, palette-tinted vignettes, and a per-hash signature chop mark

The full pipeline is documented in ALGORITHM.md.

Works in Node.js (via @napi-rs/canvas) and browsers (native Canvas API), with zero config and full determinism — the same hash produces byte-identical output every time.

Installation

npm install git-hash-art

For Node.js usage you also need the canvas backend:

npm install @napi-rs/canvas

Browser usage requires no additional dependencies.

Quick start

Node.js

import { generateImageFromHash, saveImageToFile } from 'git-hash-art';

const gitHash = '46192e59d42f741c761cbea79462a8b3815dd905';
const imageBuffer = generateImageFromHash(gitHash);

saveImageToFile(imageBuffer, './output', gitHash, 'my-art', 2048, 2048);

Browser

import { renderToCanvas, generateDataURL, generateImageBlob } from 'git-hash-art/browser';

// Render onto an existing canvas element
const canvas = document.getElementById('art-canvas');
canvas.width = 1024;
canvas.height = 1024;
renderToCanvas(canvas, '46192e59d42f741c761cbea79462a8b3815dd905');

// Or get a data URL for <img> tags
const dataUrl = generateDataURL(hash, { width: 512, height: 512 });

// Or a Blob for downloads/uploads
const blob = await generateImageBlob(hash, { width: 1080, height: 1080 });

CLI

# Generate from the current commit
npx git-hash-art current

# Generate from a specific hash
npx git-hash-art generate <hash>

# Custom size, output directory, or preset
npx git-hash-art generate <hash> --width 1920 --height 1080 --output ./artwork
npx git-hash-art generate <hash> --preset instagram-square

Core renderer (environment-agnostic)

Both entry points re-export renderHashArt, which accepts any standard CanvasRenderingContext2D — useful for OffscreenCanvas in Web Workers, custom canvas setups, or server-side rendering frameworks.

import { renderHashArt } from 'git-hash-art'; // or 'git-hash-art/browser'

const ctx = myCanvas.getContext('2d');
renderHashArt(ctx, '46192e59d42f741c761cbea79462a8b3815dd905', {
  width: myCanvas.width,
  height: myCanvas.height,
});

Configuration

Every option is optional — the archetype system picks tuned values per hash, and explicit config always wins over archetype defaults.

| Option | Type | Default | Description | | ------------------ | ------ | ------- | ----------------------------------------------------- | | width | number | 2048 | Canvas width in pixels | | height | number | 2048 | Canvas height in pixels | | gridSize | number | auto | Controls base shape count per layer (gridSize² × 1.5) | | layers | number | auto | Number of rendering layers | | shapesPerLayer | number | auto | Base shapes per layer (defaults to gridSize² × 1.5) | | minShapeSize | number | auto | Minimum shape size in pixels (scaled to canvas) | | maxShapeSize | number | auto | Maximum shape size in pixels (scaled to canvas) | | baseOpacity | number | auto | Starting opacity for the first layer | | opacityReduction | number | auto | Opacity reduction per layer | | customShapes | object | — | Your own shapes, merged into the generation (below) |

Custom shapes

Register your own geometry and it participates in everything — palette selection, affinity matching, render styles, nesting, echoes:

import { generateImageFromHash } from 'git-hash-art';

const imageBuffer = generateImageFromHash(hash, {
  customShapes: {
    lightning: {
      // Build a path centered on the origin — the pipeline handles
      // translate/rotate/fill/stroke. Use the provided rng (seeded
      // from the hash) instead of Math.random() to stay deterministic.
      draw: (ctx, size, rng) => {
        const s = size / 2;
        ctx.moveTo(-s * 0.2, -s);
        ctx.lineTo(s * (0.1 + rng() * 0.2), -s * 0.1);
        ctx.lineTo(-s * 0.1, -s * 0.1);
        ctx.lineTo(s * 0.2, s);
        ctx.lineTo(-s * (0.1 + rng() * 0.2), s * 0.1);
        ctx.lineTo(s * 0.1, s * 0.1);
        ctx.closePath();
      },
      profile: {
        tier: 1,
        heroCandidate: true,
        affinities: ['star', 'triangle'],
        bestStyles: ['fill-and-stroke', 'double-stroke'],
      },
    },
  },
});

Preset sizes

import { PRESETS, generateImageFromHash } from 'git-hash-art';

const preset = PRESETS['instagram-square'];
const imageBuffer = generateImageFromHash(preset.hash, preset);

Standard (1024²) · Banner (1920×480) · Ultrawide (3440×1440) · Instagram Square & Story · Twitter Header · LinkedIn Banner · Phone & Tablet Wallpaper · Minimal & Complex configurations

Integration recipes

GitHub Actions

name: Generate Commit Art
on: [push]
jobs:
  generate-art:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm install git-hash-art @napi-rs/canvas
      - run: npx git-hash-art current --output artwork/

Git hook

#!/bin/sh
# .git/hooks/post-commit
hash=$(git rev-parse HEAD)
npx git-hash-art generate $hash --output .git/artwork/

React component

import { useEffect, useRef } from 'react';
import { renderToCanvas } from 'git-hash-art/browser';

function CommitArt({ hash, width = 256, height = 256 }) {
  const canvasRef = useRef(null);

  useEffect(() => {
    if (canvasRef.current) {
      renderToCanvas(canvasRef.current, hash, { width, height });
    }
  }, [hash, width, height]);

  return <canvas ref={canvasRef} width={width} height={height} />;
}

Use cases

  • Commit / release art — a visual identity for every version you ship
  • Deterministic avatars & placeholders — hash any string (user IDs, file digests) for stable, unique imagery
  • Repo identicons — like the gallery, give every project a face
  • Wallpapers & social banners — built-in presets for every common size

Contributing

Issues and pull requests are welcome — see CONTRIBUTING.md. The rendering pipeline is documented in depth in ALGORITHM.md if you want to understand (or extend) how images are made.

License

MIT © Griffen Fargo