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

@theresamclaird/playing-cards

v0.1.2

Published

Zero-dependency TypeScript library for modeling playing cards: decks, multi-deck shoes, shuffle, cut, deal, and more.

Readme

@theresamclaird/playing-cards

A zero-dependency TypeScript toolkit for modeling playing cards: build decks and multi-deck shoes, then shuffle, cut, draw, and deal. A pure functional core (immutable, tree-shakeable, framework-agnostic) with an optional stateful Shoe class for game code. Ships ESM, CJS, and type definitions.

Install

npm install @theresamclaird/playing-cards

Quick start

Functional API (immutable)

Every function returns new arrays and mutates nothing, so results drop straight into React state.

import { createDeck, shuffle, cut, deal, createRng } from "@theresamclaird/playing-cards";

const deck = createDeck();                 // standard 52-card deck
const rng = createRng(42);                 // seeded → reproducible
const shuffled = cut(shuffle(deck, rng), { fraction: 0.5 });

const { hands, rest } = deal(shuffled, { hands: 4, perHand: 5 });
// hands: Card[][] (4 hands of 5), rest: the undealt remainder

Stateful Shoe class

import { Shoe } from "@theresamclaird/playing-cards";

const shoe = new Shoe({ decks: 6, cutCard: 52 }); // casino blackjack shoe
shoe.shuffle().cut();

shoe.burn(1);                       // burn a card
const player = shoe.draw(2);
const dealer = shoe.draw(2);

shoe.discard(player).discard(dealer);
if (shoe.needsReshuffle) shoe.reshuffle();

Cards

A Card is a plain object:

interface Card {
  rank: string;        // "A", "2".."10", "J", "Q", "K", or "JOKER"
  suit: string | null; // "S" | "H" | "D" | "C"; null for jokers
  id: string;          // stable, unique — safe as a React key
  deck: number;        // source deck index (0 for a single deck)
}

id is unique even across duplicate cards: the first ace of spades is "AS", the next (another deck, or a Pinochle double) is "AS#1". Compare on rank/suit; never parse id.

Deck specs

Build any deck with a DeckSpec ({ ranks, suits, jokers? }). The order of ranks defines strength, low to high. Presets included:

  • STANDARD_52 — A,2..10,J,Q,K × four suits
  • EUCHRE_24 — 9,10,J,Q,K,A × four suits
  • PINOCHLE_48 — 9,J,Q,K,10,A doubled × four suits
import { createDeck, STANDARD_52 } from "@theresamclaird/playing-cards";

const withJokers = createDeck({ ...STANDARD_52, jokers: 2 }); // 54 cards

Randomness

createRng(seed?) returns a seedable generator (mulberry32). The same seed always produces the same sequence — ideal for tests, replays, and deterministic multiplayer. Every randomized function accepts an optional Rng; the default is Math.random. For cryptographic randomness, pass your own crypto.getRandomValues-backed function.

API summary

| Function | Description | | --- | --- | | createDeck(spec?) | One ordered deck (default STANDARD_52). | | createShoe(deckCount, spec?) | Several decks combined, with unique ids. | | shuffle(cards, rng?) | Unbiased Fisher–Yates shuffle. | | cut(cards, opts) | Cut by position, fraction, or random range. | | draw(cards, n?) | Draw n from the top; clamps on over-draw. | | deal(cards, { hands, perHand, mode? }) | Deal round-robin (default) or sequential; clamps. | | rankValue / compareCards / sortHand | Rank strength and sorting helpers. | | createRng(seed?) | Seedable RNG. | | Shoe | Stateful pile with discard, reshuffle, burn, peek, cut card. |

Over-draw policy

Asking for more cards than remain returns what's left rather than throwing — running low is normal game flow. Genuinely invalid arguments (negative counts, fraction outside 0–1, NaN) throw a RangeError.

License

MIT © Theresa McLaird