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

@memizy/multiplayer-sdk

v0.4.1

Published

TypeScript SDK for building Memizy multiplayer plugins. Penpal-based RPC, mutative-based state sync, namespaced protocol (sys/settings/room/game).

Readme

@memizy/multiplayer-sdk

TypeScript SDK for building Memizy multiplayer plugins — teacher/host on a projector, players on their phones, one OQSE set, one shared lobby.

Live example sandbox: memizy.github.io/multiplayer-sdk

Documentation

Full documentation lives in docs/:

  • Plugin Developer Guide — the API surface, lifecycle, manager-by-manager reference, late-join / reconnect / teams patterns, standalone mode, and a fully worked quiz walkthrough.
  • Host Protocol Reference — the Penpal wire protocol, HostApi / PluginApi contracts, payload shapes, sequence diagrams and forward-compatibility rules.

Features

  • Penpal-based RPC — plugins call typed async functions on the host (and vice versa); no hand-rolled postMessage envelopes.
  • Mutative-based state sync — authoritative state is broadcast as tiny JSON patches instead of resending 500 kB snapshots on every tick.
  • Namespaced protocolsys / settings / room / game domains keep system chatter separate from gameplay traffic.
  • Strict role model — one SDK bundle, two API surfaces (sdk.host vs sdk.player) with compile-time and runtime role guards.
  • Content is read-only — multiplayer plugins consume OQSE items; editing and asset uploads belong to the single-player SDK.
  • Standalone mode out of the box — a branded landing page and an in-memory MockHost let you run a plugin locally with zero Memizy dependencies.

Package version: 0.4.1 · Protocol version: 0.4


Example plugin

A complete, deployable reference plugin lives in example/: a multiplayer quiz with teams, live leaderboard, timed rounds, late join, reconnect and a fully working standalone harness you can run in your browser.

npm install
npm run example:dev

The example ships with a separate minimal variant (minimal.html) for the tiniest possible working plugin, and is laid out to deploy to GitHub Pages unchanged via npm run example:build.

See example/README.md for screenshots, URL params and the list of demoed SDK features.


Installation

npm install @memizy/multiplayer-sdk

60-second overview

A multiplayer session walks through four phases; the host application drives every transition, the plugin observes them:

host-settings  ──►  synchronizing  ──►  playing  ──►  finished

One SDK bundle runs on both a host device (teacher / projector) and every player device. init.role tells each instance which manager surface to use.

import { MemizyMultiplayerSDK } from '@memizy/multiplayer-sdk';

interface QuizState {
  currentIndex: number;
  scores: Record<string, number>;
}

const sdk = new MemizyMultiplayerSDK<QuizState>({
  id: 'com.example.quiz',
  version: '1.0.0',
});

sdk.onInit(async (init) => {
  if (init.role === 'host') {
    await sdk.settings.update((draft) => {
      (draft as Record<string, unknown>).roundTimeSec ??= 15;
    });
    await sdk.settings.setValid(true);
  } else {
    renderLobby(init);
    await sdk.room.clientReady();
  }
});

sdk.onStartGameRequested(async () => {
  await sdk.host.setState<QuizState>({ currentIndex: -1, scores: {} });
});

sdk.onPlayerReady(async (playerId) => {
  if (allReady()) await sdk.room.startGame();
});

sdk.onPlayerAction(async (playerId, action) => {
  if (action.type !== 'answer') return;
  await sdk.host.updateState<QuizState>((draft) => {
    draft.scores[playerId] = (draft.scores[playerId] ?? 0) + 10;
  });
});

sdk.onState((state) => render(state as QuizState));

await sdk.connect();

The detailed versions of these patterns — with teams, late join, reconnect, transient events and error handling — live in docs/plugin-developers.md.


Protocol at a glance

The SDK exposes two namespaced APIs; the full list of methods and payloads is specified in docs/host-protocol.md.

Plugin → Host (HostApi)

| Domain | Method | Role(s) | | ----------- | ------------------------------------ | ------- | | sys | sysReady, sysRequestResize, … | Both | | settings | settingsReplace / ApplyPatches / SetValid | Host | | room | roomClientReady / HostReady / StartGame | Both | | game | gameBroadcastState / StatePatches / SendStateTo / SendEvent / EndSession | Host | | game | gameSendAction | Player |

Host → Plugin (PluginApi)

| Method | Who receives it | | ---------------------------------- | ---------------------------------------- | | onConfigUpdate / onSessionAborted / onPhaseChange | Both | | onPlayerJoin / onPlayerLeave / onPlayerReady / onPlayerAction / onStartGameRequested | Host | | onState / onStatePatches / onEvent / onGameEnd | Player |


Manifest

Declare multiplayer capabilities under appSpecific.memizy.multiplayerSdk:

{
  "capabilities": { "actions": ["render"], "types": ["mcq-single"] },
  "appSpecific": {
    "memizy": {
      "multiplayerSdk": {
        "apiVersion": "0.4",
        "minimumHostApiVersion": "0.4",
        "players": { "min": 2, "max": 60, "recommended": 30 },
        "supportsLateJoin": true,
        "supportsReconnect": true,
        "supportsTeams": false,
        "requiresHostScreen": true,
        "clientOrientation": "portrait"
      }
    }
  }
}

Use readMultiplayerConfig(manifest) inside a plugin to extract this block in a typed way. requiresHostScreen and clientOrientation are enforced by the host application, not the plugin.


Standalone mode

Plugins opened directly in a browser (not inside a Memizy iframe) fall back to an in-memory MockHost and render a branded landing page with "Try as host" / "Try as player" buttons. This is what makes the example deployable on GitHub Pages.

import {
  MemizyMultiplayerSDK,
  renderLandingPageIfNeeded,
  loadManifestFromDataIsland,
} from '@memizy/multiplayer-sdk';

const manifest = loadManifestFromDataIsland();
renderLandingPageIfNeeded(manifest, {
  docsUrl: 'https://learn.memizy.com/multiplayer',
  onTryHost: () => bootstrap('host'),
  onTryPlayer: () => bootstrap('player'),
});

async function bootstrap(role: 'host' | 'player'): Promise<void> {
  const sdk = new MemizyMultiplayerSDK({ id: manifest!.id, version: '1.0.0' });
  await sdk.connect({
    mode: 'standalone',
    standalone: {
      role,
      items: SAMPLE_ITEMS,
      assets: {},
      settings: { roundTimeSec: 15 },
      players: [
        { id: 'alice', name: 'Alice', joinedAt: Date.now() },
        { id: 'bob',   name: 'Bob',   joinedAt: Date.now() },
      ],
    },
  });
}

Errors

Every runtime guard throws a typed error you can discriminate:

  • SdkNotReadyError — manager accessed before connect() resolved.
  • SdkRoleError — host-only (or player-only) method called from the wrong role.
  • SdkPhaseError — phase-restricted method called outside its window.
  • SdkDestroyedError — any call after destroy().

License

MIT