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

@harvis/asteroids

v1.0.0

Published

Blow up any web page. A zero-dependency asteroids easter egg that turns DOM elements into targets. ~5kB gzipped, all vector art, no image assets.

Readme

@harvis/asteroids

Blow up any web page. Drop this in your project, wire it to the Konami code, and anyone who finds it gets a little spaceship that shoots the page apart — every paragraph, card and table cell is a target.

A modern rewrite of erkie/asteroids, the bookmarklet that has been destroying the web since 2010.

  • Zero runtime dependencies. ~5 kB gzipped, ESM, tree-shakeable.
  • No image assets. Every pixel is SVG path data drawn to a canvas — nothing to host, nothing to 404.
  • Contained. Canvas and HUD live in a shadow root, so page CSS cannot touch them and they cannot leak.
  • Reversible. Quitting puts the page back exactly as it was.
  • SSR safe. Calling it without a DOM does nothing.

Install

npm install @harvis/asteroids

Use

import { asteroids, konami } from '@harvis/asteroids'

konami(() => asteroids())

That is the whole easter egg. konami fires on ↑ ↑ ↓ ↓ ← → ← → B A and returns an unsubscribe function.

Prefer your own trigger? asteroids() starts a game from anywhere — a hidden menu item, an ?asteroids query param, five clicks on the logo.

Without a bundler

<script src="https://unpkg.com/@harvis/asteroids/dist/asteroids.global.js"></script>
<script>
	HarvisAsteroids.konami(() => HarvisAsteroids())
</script>

The published package also contains dist/bookmarklet.txt — paste it into a bookmark to play on any site.

Load it only when it is needed

An easter egg should not cost your users anything until they find it:

import { konami } from '@harvis/asteroids'

konami(async () => {
	const { asteroids } = await import('@harvis/asteroids')
	asteroids()
})

Controls

| Key | Action | | ----------------- | ----------------------------------- | | / W | Thrust | | / A D | Turn | | / S | Brake | | space | Fire | | B (hold) | Outline everything that can be shot | | Esc | Quit |

Fly off an edge and the page scrolls, so the whole document is playable.

Options

asteroids({
	ship: 'classic',        // 'classic' | 'dart' | 'kite' | your own SVG path data
	size: 30,               // ship size in pixels
	hud: true,              // score panel
	hints: true,            // key hints inside the score panel
	scroll: true,           // scroll the page when the ship leaves an edge
	restoreOnQuit: true,    // put every destroyed element back on exit
	safe: '.keep-me',       // selector for elements that cannot be shot
	theme: {
		ship: '#ffffff',
		outline: '#101014',
		bullet: '#ff4d2e',
		particles: ['#ff4d2e', '#ffc400'],
		reveal: '#ff4d2e',
	},
	onHit: (element, score) => {},
	onQuit: (score) => {},
})

Protecting elements

Bullets pass straight through anything matching the safe selector, and through anything carrying data-asteroids-safe — including its children:

<nav data-asteroids-safe>…</nav>

Use it for the checkout button, not for the marketing copy.

Custom ships

Ships are SVG path data in a 32×32 box with the nose pointing right:

asteroids({ ship: 'M30 16L4 2L12 16L4 30Z' })

The same strings are exported for reuse in your own markup:

import { SHIPS, svgMarkup } from '@harvis/asteroids'

document.querySelector('.logo').innerHTML = svgMarkup(SHIPS.dart, { size: 24, fill: '#ff4d2e' })

API

| Export | Description | | ----------------------------- | ------------------------------------------------------------------------------ | | asteroids(options?) | Starts a game, returns a handle. Returns the running game if one is already up. | | game.score / game.running | Current state. | | game.quit() | Stop, tear down, restore the page. | | game.restore() | Put everything shot so far back, without stopping. | | isPlaying() | Is a game running right now? | | quit() | Stop the running game, if any. | | konami(handler, options?) | ↑ ↑ ↓ ↓ ← → ← → B A. Returns an unsubscribe function. | | sequence(codes, handler) | Same, for any KeyboardEvent.code sequence. | | SHIPS, SHIP_PATH, ICONS | The artwork, as SVG path data. | | svgMarkup, svgDataUri | Turn that path data into <svg> markup or a data URI. |

Only one game runs at a time; a second asteroids() call returns the game already in progress.

Notes

  • The game takes over the arrow keys, space, B and Esc while it runs, and hands them back when it quits.
  • Elements are removed, not hidden, so scripts observing them will see them go. restoreOnQuit (on by default) reinserts them in place on exit.
  • Rendering pauses in background tabs, like anything driven by requestAnimationFrame.
  • TypeScript types are bundled.

Develop

npm run dev     # demo page on http://localhost:5173
npm run build   # dist/ + size report

Releasing

Publishing runs in CI (.github/workflows/publish.yml).

One-time setup: create an automation access token on npmjs.com (Access Tokens → Generate → Automation, so it bypasses 2FA) and add it to the repository as the NPM_TOKEN secret.

To ship a version:

npm version patch   # or minor / major — commits and tags
git push --follow-tags

Then publish a GitHub release for that tag. The workflow installs, builds, checks that the tag matches package.json and that the version is not already on the registry, and publishes with provenance attested to this repo and commit.

workflow_dispatch runs the same job manually and takes a dist-tag input, for next or beta releases.

Credits

Idea, game design and the original implementation: Erik Rothoff Andersson and Rootof Creations HB — erkie/asteroids. This is an altered version with a rewritten engine, distributed under the same zlib license. See LICENSE.