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

tinyroom

v0.1.0

Published

A minimalistic room/lobby system for multiplayer applications. Built on TinyPeer with automatic reconnection support.

Readme

TinyRoom

npm version license bundlephobia minzipped size

[!WARNING] This project is in early development. The API may change before a stable 1.0 release. Use with caution in production.

A tiny, host‑authoritative P2P room/lobby for the browser. Built on TinyPeer with PeerJS Server signaling. One peer hosts the room (authority) and everyone, including the host, joins as a client.

This gives a clean separation of concerns: write client code as if everyone talks to a single remote server.

Why TinyRoom?

  • Host‑authoritative rooms – One peer acts as the room/server; same client API for everyone (host included)
  • No server – Uses a public signaling server (PeerJS) only; messages sent peer-to-peer over WebRTC data channels
  • Reconnect that sticks – Optional layer with health checks, grace periods, and stable IDs across refreshes
  • Small and focused – Minimal bundle, zero deps beyond TinyPeer
  • Flexible by design – Start with basic rooms; add reconnection when you need it

Great for multiplayer games, collaborative tools, and real‑time apps where a single peer manages connections and game state.

Installation

npm install tinyroom

Quick Start

import { joinOrCreateRoom } from 'tinyroom'

const { room, client } = await joinOrCreateRoom('game-123', {
  metadata: { username: 'Alice' }
})

if (room) {
  // Handle peers connecting
  room.onPeerJoin((peer) => {
    console.log(`${peer.id} joined`)
  })
  
  // Handle messages from clients
  room.on('move', (data, fromId) => {
    // Validate and relay to everyone
    room.broadcast('move', { player: fromId, ...data })
  })
}

// All players can have the same client code (including host!)
client.send('move', { x: 5, y: 10 })
client.on('move', (data) => {
  console.log(`Player ${data.player} moved`)
})

API

Factory Functions

createRoom(roomId, options?)

Creates a new room with the caller as host.

import { createRoom } from 'tinyroom'

const { room, client } = await createRoom('my-room', {
  metadata: { username: 'Alice' },  // Optional: peer metadata
  // ... all TinyPeer options (host, port, path, etc.)
})

Returns: { room: HostRoom, client: Client }

  • room - Room management interface (host only)
  • client - Client interface for the host (hosts participate as a regular player too)

joinRoom(roomId, options?)

Joins an existing room as a client.

import { joinRoom } from 'tinyroom'

const client = await joinRoom('my-room', {
  metadata: { username: 'Bob' }
})

Returns: Client

Throws: If room doesn't exist or connection fails.

joinOrCreateRoom(roomId, options?)

Attempts to join a room, creates it if it doesn't exist.

import { joinOrCreateRoom } from 'tinyroom'

const { client, room } = await joinOrCreateRoom('my-room', {
  metadata: { username: 'Charlie' }
})

if (room) {
  console.log('Created room - I am host')
  room.onPeerJoin((peer) => {
    console.log(`${peer.id} joined`)
  })
} else {
  console.log('Joined existing room')
}

// Everyone uses client the same way
client.send('hello', { message: 'Hi!' })

HostRoom Interface

The room object provides host-only management capabilities.

Properties:

  • id: string - Room ID (same as host's peer ID)
  • peers: Map<string, Peer> - All connected peers (including host)

Methods:

room.on(event, handler)

Receives custom messages from all clients.

room.on('move', (data, fromId, metadata) => {
  console.log(`Client ${fromId} wants to move`, data)

  // Validate, process, then broadcast
  if (isValidMove(data)) {
    room.broadcast('move', { player: fromId, ...data })
  }

  console.log('Optional metadata from client', metadata)
})
room.broadcast(event, data, metadata?)

Send message to all connected clients (including host's own client).

await room.broadcast('game-start', { level: 1 })
room.sendToPeer(event, data, target, metadata?)

Send message to specific client(s).

// Single target
await room.sendToPeer('welcome', { msg: 'Hi!' }, 'peer-123')

// Multiple targets
await room.sendToPeer('team-update', data, ['peer-1', 'peer-2'])
room.ping(peerId)

Measure latency to a specific peer.

const latency = await room.ping('peer-123')
console.log(`Latency: ${latency}ms`)
room.onPeerJoin(handler)

Called when a peer joins. Immediately fires for host when handler first set.

room.onPeerJoin((peer) => {
  console.log(`${peer.id} joined`, peer.metadata)
  console.log(`Is host: ${peer.isHost}`)

  // Kick a peer
  if (shouldKick(peer)) {
    peer.close('You are not allowed')
  }
})

The handler receives:

  • peer.id - Peer's unique ID
  • peer.metadata - Connection metadata
  • peer.isHost - Whether this is the host
  • peer.close(reason?) - Function to disconnect the peer
room.onPeerLeave(handler)

Called when a peer permanently leaves.

room.onPeerLeave((peerId) => {
  console.log(`${peerId} left`)
  removeFromGame(peerId)
})
room.close()

Closes the room and disconnects all clients.

await room.close()

Client Interface

The client object is used by everyone (including host) for player behavior.

Properties:

  • id: string - This client's peer ID

Methods:

client.send(event, data, metadata?)

Send message to host (who will process/relay it).

await client.send('move', { x: 10, y: 20 })
client.on(event, handler)

Receive messages (broadcasts from host).

client.on('move', (data, fromId, metadata) => {
  console.log(`Player ${fromId} moved to`, data)
  updateSprite(fromId, data.x, data.y)

  console.log('Optional metadata from client', metadata)
})
client.ping()

Measure latency to host (client only, host should use room.ping(peerId)).

const latency = await client.ping()
console.log(`Latency to host: ${latency}ms`)
client.onClose(handler)

Called when connection closes.

client.onClose((reason) => {
  console.log('Connection closed:', reason)
})
client.onError(handler)

Called on connection errors.

client.onError((error) => {
  console.error('Connection error:', error)
})
client.leave()

Disconnect from the room.

client.leave()

Reconnection (Experimental)

TinyRoom is experimenting with reconnection support with stable peer identities.

See reconnect.md for full documentation.

Advanced Topics

Host as Player

The host uses their client object just like everyone else:

const { room, client } = await createRoom('game-123')

// Host sends moves as a client
client.send('move', { x: 10, y: 10 })

// This triggers the room.on() handler
room.on('move', (data, fromId) => {
  // fromId will be the host's own ID when host sends
  room.broadcast('move', { player: fromId, ...data })
})

// Host receives broadcasts like everyone else
client.on('move', (data) => {
  updatePlayerSprite(data.player, data.x, data.y)
})

Message Routing

Client → Host:

client.send('move', data)  // Goes to host

Host receives and processes:

room.on('move', (data, fromId) => {
  // Validate, process, then broadcast
  room.broadcast('move', { player: fromId, ...data })
})

Everyone receives broadcast:

client.on('move', (data) => {
  // Both host and clients receive this
})

Examples

Basic Room

See the basic room example for a complete working demo.

Reconnection

See the reconnection example for a complete working demo with:

  • Host controls (simulate disconnect, kick peer)
  • Client reconnection UI
  • Health checks
  • Grace periods
  • All lifecycle events

Browser Support

Requires modern browsers with WebRTC support (ES2020+).

TinyRoom uses modern APIs and assumes recent browser versions. If you encounter browser-specific issues, please open an issue.

License

MIT

Built With

  • TinyPeer - Minimalistic WebRTC peer-to-peer library

Acknowledgements

Inspired by: