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 🙏

© 2025 – Pkg Stats / Ryan Hefner

whackrow

v0.1.7

Published

JS dev kit for game+controls over WebSocket with Vite dev server.

Readme

whackrow

Vanilla JS dev kit for building Whackrow-compatible games locally with a WebSocket loop for controls.

Quick commands (no setup in your app)

  • npx whack — scaffolds demo/index.html, demo/game.html, demo/controls.html, and root-level main.js + ControlsClient.js
  • npx whack-controls — starts the local WebSocket relay at ws://localhost:3030 (also prints ws://LAN_IP:3030)
  • npx whack-dev — starts the dev server and serves demo at local and LAN URLs

Local development (optional scripts)

  1. Start the WS server: npx whack-controls
  2. Start the dev server: npx whack-dev
  3. Open http://localhost:5173/
    • Game shows a QR code pointing to Controls on your LAN origin (so phones can connect)
    • Controls page connects to the WS and exposes a v‑stick (WASD/Arrows also work)

Important: use LAN URL (not localhost) on phone

  • When testing across devices, open the Game and Controls pages using the LAN URL printed by the dev server (e.g. http://192.168.x.x:5173).
  • The phone cannot reach localhost, so the QR and the WebSocket must both target your LAN IP (e.g. ws://192.168.x.x:3030).

Game module API (demo/main.js)

Your game module should export startGame with the following runtime contract (the scaffolder generates demo/main.js based on this):

export async function startGame({ canvas, onTimerUpdate, onPerformanceUpdate, onWinner, onPlayerEliminated } = {}) {
  // create render loop, keep internal state
  return {
    ensurePlayer(id) {},
    setJoystickFor(id, payload) {},
    reset() {},
    pause() {},
    resume() {},
  };
}

Controls client usage

<script type="module">
  import { ControlsClient } from './ControlsClient.js'
  const client = new ControlsClient({ url: 'ws://YOUR_HOST:3030', playerId: 'PHONE_123' })
  client.connect()
  client.attachVirtualJoystick(document.getElementById('joystick'))
  // also supports keyboard (WASD/Arrows)
</script>

Payload format (sent from controller)

{
  "type": "move",
  "vector": { "x": 0.96, "y": 0.28 },
  "distance": 1,
  "angle": { "radian": 0.28, "degree": 16.24 },
  "direction": "right",
  "playerId": "optional-id"
}

The demo game uses vector to move a Whackrow logo on a fullscreen canvas. Add ?player=PHONE_XXXX to Controls URL to include a playerId.

Publishing on Whackrow

  • Locally, you use the provided WebSocket relay and ControlsClient to simulate inputs.
  • When your game is published on the Whackrow platform, Whackrow’s engine manages controllers and calls your exported functions from main.js directly (e.g., ensurePlayer, setJoystickFor, reset, pause, resume). You won’t need the local WS relay in production.

Important compatibility notice

  • Whackrow does not guarantee behavior if you change the function signatures or internal logic of the scaffolded main.js API or the ControlsClient joystick wiring. These functions are invoked by the Whackrow engine in production.
  • Recommendation: Treat the exported API as stable; build your game inside those hooks rather than modifying the API itself.

Notes

  • The QR in game.html uses your LAN origin from the dev server. If no LAN IP is detected, it falls back to location.origin.
  • You can override the WS URL via ?ws=ws://<host>:3030 on both pages.

API Reference

Game module (main.js)

Export a single asynchronous function startGame that initializes your game and returns a control API.

Signature

export async function startGame(options?: {
  canvas?: HTMLCanvasElement,
  onWinner?: (p: { winnerId: string }) => void,
  onPlayerEliminated?: (playerId: string) => void,
  playerColorResolver?: (id: string, ctx: { usedColors: Set<string>, availableColors: string[] }) => string | void,
  onTimerUpdate?: (p: { remainingMs: number }) => void,
  onPerformanceUpdate?: (p: { fps: number, targetFps: number }) => void,
  onBump?: (p: { attacker: string | null, victim: string | null, strength: number, victimOpposite: boolean }) => void
}): Promise<{
  ensurePlayer: (id: string) => void,
  setJoystickFor: (id: string, payload: JoystickPayload) => void,
  reset: () => void,
  pause: () => void,
  resume: () => void
}>

Parameters

  • canvas (optional): The target canvas to render into. If omitted, your module may query #gameCanvas.
  • onWinner (optional): Notify platform when a winner is decided.
  • onPlayerEliminated (optional): Notify when a player is eliminated.
  • playerColorResolver (optional): Deterministically choose a player color.
  • onTimerUpdate (optional): Report a visible game timer (ms remaining).
  • onPerformanceUpdate (optional): Report current fps and target (30 or 60).
  • onBump (optional): Report physics “bump” events with metadata.

Returned control API

  • ensurePlayer(id): Create/spawn the player with identifier id if missing.
  • setJoystickFor(id, payload): Apply latest joystick input for id.
  • reset(): Reset state for a new round (clear players, timers, etc.).
  • pause(): Pause the game loop.
  • resume(): Resume the game loop.

JoystickPayload

type JoystickPayload = {
  type: 'move',
  vector: { x: number, y: number }, // normalized -1..1
  distance: number,                  // magnitude 0..1
  angle: { radian: number, degree: number },
  direction: 'up' | 'down' | 'left' | 'right',
  playerId?: string
}

Notes

  • vector represents the instantaneous input direction and magnitude (normalized to unit circle). You typically multiply by a speed and dt to move entities.
  • distance is the magnitude [0..1]. If you use vector directly, distance is typically redundant; it is provided for convenience and UI effects.
  • direction is a coarse 4-way direction computed from angle and can be useful for animations.

ControlsClient (ControlsClient.js)

Constructor

new ControlsClient(options?: { url?: string, throttleMs?: number, playerId?: string })

Options

  • url (default ws://localhost:3030): WebSocket endpoint to connect to.
  • throttleMs (default 16): Minimum ms between outgoing joystick messages (client-side rate limit).
  • playerId (optional): If set, the client includes this id in all messages.

Methods

  • connect()
    • Opens the WebSocket. Sends { type: 'player_connected', role: 'controls', playerId? } on open.
  • disconnect()
    • Closes the WebSocket and removes keyboard/joystick handlers.
  • attachVirtualJoystick(container: HTMLElement)
    • Renders a touch joystick “knob” inside container. Pointer/touch input is clamped to a 70px radius and published continuously via RAF.
  • detachVirtualJoystick()
    • Removes joystick handlers and UI.

Keyboard support

  • WASD and Arrow keys are supported automatically after connect(). Keyboard input generates the same joystick payloads as the virtual joystick.

Outgoing messages (from ControlsClient)

  • On connect: { type: 'player_connected', role: 'controls', playerId? }
  • Joystick: JoystickPayload (see schema above). Messages are throttled by throttleMs and also emitted continuously while dragging.

Local WebSocket relay

  • Start with npm run controls. The server prints both ws://localhost:3030 and ws://<LAN_IP>:3030 if available.
  • Behavior: it relays any text message to all other connected clients. This approximates multi-controller dev locally.
  • Scope: this WebSocket relay and the ControlsClient are for local development only. Do not ship or rely on the WS relay in production. On the Whackrow platform, controllers are managed by the platform and your game receives inputs via direct calls to your main.js API (e.g., ensurePlayer, setJoystickFor).