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

@castarcade/game-runtime

v0.3.1

Published

The browser runtime Cast Arcade uses to mount and drive games.

Readme

@castarcade/game-runtime

Standalone browser ESM host for the Cast Arcade beta bridge. Its shipped JavaScript uses only relative module imports and browser APIs. Type declarations need no private repository packages. Install with npm install @castarcade/game-runtime, or serve all three JavaScript files together.

import { mountGame } from '@castarcade/game-runtime';
const game = mountGame(document.querySelector('#game'), {
  url: 'https://your-platform.example/api/community/v1/assets/BUILD/index.html',
  manifest, // Use a server-validated GameManifest, policy/SDK version 1.
  mode: 'shared-screen',
  playerCount: 2,
  onResult(result) { results.textContent = JSON.stringify(result); },
  onError(error) { status.textContent = error.message; },
});
game.pause();
game.resume();
game.setMuted(true);
game.dispose(); // Idempotent; always call when leaving the player.

For a Cast Arcade room, pass the fixed room roster and use controls: 'managed':

const game = mountGame(container, {
  url,
  manifest,
  mode: 'shared-screen',
  players: roomPlayers,
  controls: 'managed',
});

game.applyInput({ playerId, actionId, value, pressed });

Managed controls accept authorized phone actions through applyInput and local keyboard, touch, or browser gamepad input. Bluetooth and wired controllers both appear through the browser Gamepad API after the operating system connects them. Cast Arcade handles the room and phone controls; partner games handle the declared semantic actions.

mode defaults to solo when supported; otherwise shared-screen. playerCount defaults to one for solo, or the minimum supported shared-screen count (at least two). Counts must fit the manifest and are capped at four. The returned players (p1–p4, names Player 1–Player 4) and mode describe the exact started session. Changing configuration requires disposing and mounting again. loadTimeoutMs defaults to 15000; pointerLock:true only adds allow-pointer-lock.

The iframe always uses sandbox="allow-scripts" without same-origin access. Ready messages must come from that exact frame, have opaque origin null, and match protocol version 1. A dedicated MessageChannel carries lifecycle, viewport, semantic inputs and bounded results. Score or elapsed-time rows must refer to started players and match the manifest's declared record kind. Optional metadata uses the same account-safe scalar rules described by the SDK. Browser results may feed private personal history, but they are not verified public records or trusted economic events. Dispose closes ports, removes listeners/frame, disconnects ResizeObserver, and cancels timers and gamepad polling. Page visibility pauses and conditionally resumes; blur/disconnect neutralizes held input.

The integrating host must serve games with createArtifactHeaders from the public game contract and restrict its own page CSP frame-src to the exact approved artifact directory. The library cannot set its embedding document's HTTP CSP. Keep preview capability paths out of logs. Do not expose credentials or account identity to frames.

Keyboard maps the first two axis actions then first two button actions in manifest order:

| Player | Axis 1 | Axis 2 | Buttons | |---|---|---|---| | 1 | Left/Right | Up/Down | Space, Enter | | 2 | A/D | W/S | F, G | | 3 | J/L | I/K | O, P | | 4 | Numpad 4/6 | Numpad 8/2 | Numpad 0, Decimal |

Touch buttons support every declared action and are rendered as text in a separate host panel. Axis controls have minus/plus buttons. Gamepad connection slots 1–4 map to players 1–4; axis/button actions map in manifest order with a 0.15 axis dead zone. Press a physical controller button to activate browser gamepad access. Standard controllers may lack actions beyond four axes/sixteen buttons; guidance points to touch or game-specific controls. Missing gamepad APIs are reported visibly.

Iframe focus: keyboard events inside an opaque frame do not bubble to its host. Games must implement native keyboard handling using these same assignments in addition to bridge inputs; clear native input on blur/pause and prevent duplicate default button activation. Both provided CLI starters do this. Imported games need explicit adaptation. Touch and gamepad bridge inputs continue to work with the frame focused.

Unit tests exercise a real SDK instance through paired test ports plus input, shape and cleanup behavior. Browser verification is a separate manual check; these tests do not claim actual device coverage.

For manifests declaring play.privateViews: true, onControllerView(view) receives a validated { playerId, title?, text?, actions?: [{ id, label? }] }. Route it only to that authorized seat and render its strings as text. A view with only playerId clears existing content. Runtime and SDK both enforce declared actions, fixed roster, 4 KiB serialized size and bounded text lengths.

For play.checkpoint: 'versioned', onCheckpoint(checkpoint) receives validated { version, state } JSON up to 32 KiB. Persist it under the immutable release and session, then supply checkpoint when mounting that same session. Runtime snapshots and validates it before creating an iframe. The SDK invokes onRestore before start and buffers input while an asynchronous restore completes. Restore failure reaches onError and disposes the frame. Checkpoints remain advisory client state and must never be treated as verified score evidence.

Managed local and phone input share one source-aware state reducer. Releasing or disconnecting one device preserves another device's hold; pause and disposal clear every held source.

For a validated schema v2 server: { entry, tickHz } manifest, mounted.updateState(display) accepts at most 32 KiB JSON, snapshots the data, and returns whether it was accepted. Before handshake/start it keeps only the newest valid update. After start it delivers SDK state(display) under the authoritative-session flag; undeclared or disposed hosts reject updates. The runtime never executes the server entry in the browser.