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

@norarcasey/star-siege-nora

v0.3.0

Published

Nora's Star Siege — a canvas-rendered, retro arcade space-shooter as a React component and hook.

Readme

Nora's Star Siege

A retro arcade space-shooter for React, rendered to an HTML5 canvas. Drop it into any project as a component, or drive the engine yourself with the hook.

Distinct from any particular 1978 cabinet — this is Nora's own siege.

Install

npm install @norarcasey/star-siege-nora
# react and react-dom (>=18) are peer dependencies

Quick start

import { StarSiege } from '@norarcasey/star-siege-nora';

export function Game() {
  return (
    <StarSiege
      cellSize={28}
      onWin={(score) => console.log('Cleared!', score)}
      onLose={(score) => console.log('Game over at', score)}
    />
  );
}

Arrow keys move the ship; Space or fires; P pauses and resumes. Pass keyboard={false} to wire up your own controls.

<StarSiege /> props

| Prop | Type | Default | Description | | --------------- | -------------------------- | ------- | ---------------------------------------- | | cellSize | number | 24 | Pixel size of each grid cell. | | colors | StarSiegeColors | — | Override background/invader/ship/etc. | | showStatus | boolean | true | Show the score / win / lose banner. | | keyboard | boolean | true | Bind arrow keys + space automatically. | | paused | boolean | false | Freeze the game loop. | | onWin | (score: number) => void | — | Fired once when every invader is gone. | | onLose | (score: number) => void | — | Fired once when the player is hit. | | onScoreChange | (score: number) => void | — | Fired whenever the score changes. |

Plus all GameConfig tuning fields (width, height, invaderStepMs, laserStepMs, initialInvaders, initialShooter, …).

Custom UIs with the hook

useStarSiege runs the engine on a requestAnimationFrame loop and hands you the live state plus controls — render it however you like:

import { useStarSiege } from '@norarcasey/star-siege-nora';

function Hud() {
  const { state, paused, moveLeft, moveRight, shoot, togglePause, reset } =
    useStarSiege({ keyboard: false });

  return (
    <div>
      <p>Score: {state.score} — {paused ? 'paused' : state.status}</p>
      <button onClick={moveLeft}>◀</button>
      <button onClick={shoot}>fire</button>
      <button onClick={moveRight}>▶</button>
      <button onClick={togglePause}>{paused ? 'resume' : 'pause'}</button>
      {state.status !== 'playing' && <button onClick={reset}>restart</button>}
    </div>
  );
}

Develop

npm install
npm run dev        # playground at localhost:5173
npm run build      # emits dist/ + type declarations
npm run typecheck

How it works

The core is a DOM-free GameEngine — a pure state machine that owns the grid, invader march, lasers, and win/lose rules. It starts no timers and draws nothing; callers advance it with tick(dt) and read snapshots with getState(). The React layer (hook + component) supplies the loop, input, and canvas rendering. Because the engine keeps no module-level state, multiple games can run on one page.

License

MIT © Nora Casey