changelog-invaders
v1.0.0
Published
Turn your changelog into a game - A retro Space Invaders-style arcade game React component
Maintainers
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. 🎵
🎬 See It In Action

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-invadersStep 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-invadersimport { 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
- Shoot Bugs - Destroy enemy bugs for points
- Collect Power-ups - Lightning bolts upgrade your weapons (up to level 5)
- Fly Through Gates - Pass through version stargates for speed bonuses
- Don't Let Bugs Escape - Penalties increase for each escaped bug
- 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
- 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);- 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 buildLicense
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:
- 🐦 X/Twitter: @kamilbanc
- 💻 GitHub: @kbanc85
Built with ❤️ for developers who believe changelogs should be fun.
👾 PEW PEW PEW 👾