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

changelog-invaders

v1.0.0

Published

Turn your changelog into a game - A retro Space Invaders-style arcade game React component

Readme

Changelog Invaders 🚀👾

Your changelog is boring. Let's fix that.

    ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
    █  WHAT IF YOUR USERS ACTUALLY LOOKED FORWARD TO  █
    █              READING YOUR CHANGELOG?            █
    ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀

Drop this React component into your app and watch your changelog transform into a full arcade game where users blast through bugs, collect power-ups, and fly through stargate portals representing your version milestones.

8-bit chiptune soundtrack included. 🎵

npm version License: MIT


🎬 See It In Action

Changelog Invaders Demo

Blast through bugs, collect power-ups, fly through version stargates!

🕹️ Play the Live Demo →

Try it right now on the Right Click Prompt changelog page — no install needed!


🚀 Add to Your Project in 3 Steps

Step 1: Install the package

npm install changelog-invaders

Step 2: Create a changelog page (or add to existing one)

Create a new file like app/changelog/page.tsx (Next.js) or src/pages/Changelog.tsx (Vite/CRA):

import { ChangelogInvaders } from 'changelog-invaders'

export default function ChangelogPage() {
  return (
    <ChangelogInvaders
      gameTitle="MY APP ODYSSEY"
      versions={["v1.0.0", "v1.1.0", "v2.0.0"]}
    />
  )
}

Step 3: There is no step 3. You're done! 🎉

Visit your changelog page and start playing. High scores save automatically.


Why Add This to Your Changelog?

  • 🎮 Users will actually visit your changelog — Nobody reads changelogs. Everyone plays games.
  • 🏆 Built-in engagement — High scores make users come back
  • 🚀 Ship your versions as literal stargates — v1.0 → v2.0 becomes an epic journey
  • 🐛 Make bug fixes satisfying — Your users get to literally shoot the bugs you fixed
  • 5 minutes to integrate — One component, zero config required

Originally created by Kamil Banc for Right Click Prompt and open-sourced for the community.

Features

  • 🎮 Full arcade experience - Complete game with shooting, power-ups, and boss gates
  • 🎵 Procedural chiptune audio - Heavy metal-inspired 8-bit music and sound effects
  • 🎨 Retro pixel art - CRT screen effects, scanlines, and pixel-perfect graphics
  • 🌟 Stargate portals - Fly through version gates to progress
  • 🏆 High scores - Works offline with localStorage, no database required
  • ⚙️ Fully customizable - Colors, sprites, enemies, power-ups, and more
  • 📱 Responsive - Works on any screen size
  • 🔇 Audio toggle - Players can mute/unmute with M key
  • 🔌 Zero dependencies - Only requires React, no database or backend needed

Quick Start

npm install changelog-invaders
import { ChangelogInvaders } from 'changelog-invaders'

export default function ChangelogPage() {
  return (
    <ChangelogInvaders
      gameTitle="MY APP ODYSSEY"
      versions={["v1.0.0", "v1.1.0", "v1.2.0", "v2.0.0"]}
    />
  )
}

That's literally it. Three lines of config. Your changelog is now a game with:

  • Generic spaceship sprite
  • Default color palette (blue/green/purple/amber)
  • localStorage high scores
  • Full procedural audio engine
  • All game mechanics

Configuration

Basic Props

<ChangelogInvaders
  // Required
  gameTitle="ACME ODYSSEY"           // Title on the start screen
  versions={["v1.0", "v2.0", "v3.0"]} // Version gates to fly through

  // Optional branding
  gameSubtitle="— JOURNEY TO V3 —"   // Subtitle below title
/>

Custom Colors

<ChangelogInvaders
  gameTitle="MY APP"
  versions={["v1.0", "v2.0"]}
  colors={{
    bug: "#ff6b6b",         // Blue enemies
    feature: "#4ecdc4",     // Green enemies
    improvement: "#ffe66d", // Purple enemies
    breaking: "#ff8c42",    // Amber enemies
    accent: "#6c5ce7"       // UI accent (bullets, etc.)
  }}
/>

Custom Enemies & Power-ups

<ChangelogInvaders
  gameTitle="MY APP"
  versions={["v1.0", "v2.0"]}
  enemyItems={[
    { type: "bug", text: "CRASH BUG" },
    { type: "bug", text: "MEMORY LEAK" },
    { type: "feature", text: "SCOPE CREEP" },
    { type: "improvement", text: "TECH DEBT" },
    { type: "breaking", text: "BREAKING!" },
  ]}
  powerUpItems={[
    { type: "weapon", text: "SHIPPED!" },
    { type: "weapon", text: "DEPLOYED!" },
    { type: "shield", text: "TESTED!" },
  ]}
/>

Backend Integration (100% Optional)

Note: The game works perfectly fine without any backend. High scores are automatically saved to localStorage and persist across sessions. You only need a backend if you want a global leaderboard shared across all users.

If you want a global leaderboard, provide an API endpoint:

<ChangelogInvaders
  gameTitle="MY APP"
  versions={["v1.0", "v2.0"]}
  highscoresEndpoint="/api/game/highscores"  // Only if you want global scores
/>

Your API should implement:

  • GET /api/game/highscores → Returns { highScores: HighScore[] }
  • POST /api/game/highscores → Accepts { playerName, score, gatesPassed, maxSpeed }

See the Backend Setup section for a Supabase example (or use any backend you prefer).

All Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | gameTitle | string | required | Game title on start screen | | versions | string[] | required | Version gates to fly through | | gameSubtitle | string | "" | Subtitle below title | | colors | Partial<ColorPalette> | Default palette | Custom color scheme | | shipSprite | string[] | Generic ship | Custom ship pixel art | | shipColors | ShipColors | Default colors | Color mapping for ship | | enemyItems | EnemyItem[] | Default items | Custom enemy definitions | | powerUpItems | PowerUpItem[] | Default items | Custom power-up definitions | | highscoresEndpoint | string | undefined | API endpoint for scores | | localStorageKey | string | "changelog-invaders-high-scores" | Local storage key | | enableSound | boolean | true | Enable/disable audio | | enableLeaderboard | boolean | true | Show leaderboard panel | | height | number | 280 | Canvas height in pixels |

Gameplay

Controls

| Key | Action | |-----|--------| | / A | Move left | | / D | Move right | | Space | Shoot | | M | Toggle mute | | Esc | Return to demo |

Mechanics

  1. Shoot Bugs - Destroy enemy bugs for points
  2. Collect Power-ups - Lightning bolts upgrade your weapons (up to level 5)
  3. Fly Through Gates - Pass through version stargates for speed bonuses
  4. Don't Let Bugs Escape - Penalties increase for each escaped bug
  5. Reach the Final Version - Complete the journey to win!

Scoring

  • Bugs: 10-20 points (×weapon level)
  • Power-ups: 100 points (×weapon level)
  • Gates: 150 points (×speed multiplier)
  • Time survival: 1 point every 0.5 seconds
  • Movement multiplier: Up to 5× for continuous movement
  • Victory bonus: 5000 points

Backend Setup (Optional)

You don't need this section unless you want a global leaderboard. The game works completely offline with localStorage for high scores.

Supabase Example

  1. Create a migration file:
-- supabase/migrations/001_high_scores.sql
CREATE TABLE game_high_scores (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  player_name VARCHAR(10) NOT NULL,
  score INTEGER NOT NULL,
  gates_passed INTEGER DEFAULT 0,
  max_speed DECIMAL(4,2) DEFAULT 1.0,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_high_scores_score ON game_high_scores(score DESC);

-- Row Level Security
ALTER TABLE game_high_scores ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Anyone can read high scores"
  ON game_high_scores FOR SELECT
  TO anon, authenticated
  USING (true);

CREATE POLICY "Anyone can insert high scores"
  ON game_high_scores FOR INSERT
  TO anon, authenticated
  WITH CHECK (true);
  1. Create API routes (Next.js example):
// app/api/game/highscores/route.ts
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_KEY!
)

export async function GET() {
  const { data, error } = await supabase
    .from('game_high_scores')
    .select('id, player_name, score, gates_passed, max_speed, created_at')
    .order('score', { ascending: false })
    .limit(10)

  if (error) {
    return Response.json({ error: error.message }, { status: 500 })
  }

  const highScores = data.map((row, index) => ({
    ...row,
    rank: index + 1
  }))

  return Response.json({ highScores })
}

export async function POST(request: Request) {
  const body = await request.json()
  const { playerName, score, gatesPassed, maxSpeed } = body

  const { data, error } = await supabase
    .from('game_high_scores')
    .insert({
      player_name: playerName.substring(0, 10),
      score,
      gates_passed: gatesPassed,
      max_speed: maxSpeed
    })
    .select()
    .single()

  if (error) {
    return Response.json({ error: error.message }, { status: 500 })
  }

  // Check if it's top 10
  const { count } = await supabase
    .from('game_high_scores')
    .select('*', { count: 'exact', head: true })
    .gt('score', score)

  const rank = (count || 0) + 1
  const isTop10 = rank <= 10

  return Response.json({ success: true, isTop10, rank })
}

Custom Ship Sprite

Create your own ship using pixel art strings:

const myShip = [
  "........BB........",  // B = Black outline
  ".......BWWB.......",  // W = White
  "......BWWWWB......",  // R = Red/accent
  ".....BWWRRWWB.....",  // G = Gray
  "....BWWWWWWWWB....",  // . = Transparent
  "...BWWWWWWWWWWB...",
  "..BWWWWWWWWWWWWB..",
  ".BWWWWWWWWWWWWWWB.",
  "BWWWWWWWWWWWWWWWWB",
  "BWWWGWWWWWWWWGWWWB",
  "BWWWGWWWWWWWWGWWWB",
  ".BWWGWWWWWWWWGWWB.",
  "..BWGGWWWWWWGGWB..",
  "...BGGGWWWWGGGB...",
  "....BGGGGGGGB.....",
  ".....BBBBBBB......",
]

<ChangelogInvaders
  gameTitle="MY APP"
  versions={["v1.0", "v2.0"]}
  shipSprite={myShip}
  shipColors={{
    B: "#000000",
    W: "#ffffff",
    R: "#ff0000",
    G: "#666666",
  }}
/>

TypeScript

Full TypeScript support with exported types:

import {
  ChangelogInvaders,
  ChangelogInvadersConfig,
  InvaderType,
  PowerUpType,
  ColorPalette,
  EnemyItem,
  PowerUpItem,
} from 'changelog-invaders'

const config: ChangelogInvadersConfig = {
  gameTitle: "MY APP",
  versions: ["v1.0", "v2.0"],
  // ... fully typed
}

Browser Support

  • Chrome 80+
  • Firefox 75+
  • Safari 13+
  • Edge 80+

Requires:

  • Web Audio API (for sound)
  • Canvas 2D API (for graphics)
  • ResizeObserver (for responsive sizing)

Contributing

Contributions are welcome! Please read our contributing guidelines first.

# Clone the repo
git clone https://github.com/kbanc85/changelog-invaders.git
cd changelog-invaders

# Install dependencies
npm install

# Start development
npm run dev

# Build
npm run build

License

MIT © Kamil Banc

Credits

Originally built for Right Click Prompt - a prompt management tool for AI workflows.

Created by Kamil Banc — follow for more developer tools:


Built with ❤️ for developers who believe changelogs should be fun.

        👾 PEW PEW PEW 👾