pdrng
v1.1.1
Published
Pseudo Deterministic Random Number Generator. Coin flips, dice, cards, roulette, tarot, fortunes and more, all derived from a seed. Same seed, same output.
Maintainers
Readme
pdrng
Pseudo Deterministic Random Number Generator. Same seed, same output.
A seed is any text or number. Text is hashed to a number (brian → 814, the default). From that number pdrng derives everything else: a playing card, a roulette pocket, a tarot card, a Magic 8-Ball answer, a zodiac sign, a hex color, a bingo call, dice results. The same seed always produces the same results.
Site: pdrng.com · API docs: pdrng.com/api/docs · OpenAPI: pdrng.com/api/openapi.json
curl "https://pdrng.com/api/v1/profile?seed=brian"import pdrng from 'pdrng';
pdrng(3, { seed: 'brian' }) // 814
pdrng.card() // "8 of Diamonds"
pdrng.roulette() // { number: 14, color: "red", parity: "even" }
pdrng.tarot() // "The Magician"
pdrng.magic8() // "Reply hazy, try again."
pdrng.color() // "#a81414"pdrng is a deterministic engine for games of chance, not a cryptographic random number generator.
Default seed: 814
Install
npm install pdrngQuick Start
import pdrng from 'pdrng';
pdrng() // 814
pdrng(1) // 8
pdrng(6) // 814814
pdrng.coin() // "tails"
pdrng.dice() // 4
pdrng.card() // "8 of Diamonds"API
Core
pdrng(digits?, options?)
Generate a deterministic number with the specified number of digits (1-15). Throws RangeError otherwise.
pdrng() // 814 (default: 3 digits)
pdrng(1) // 8
pdrng(2) // 14
pdrng(4) // 8148
pdrng(5) // 81414
pdrng(6) // 814814Utilities
float(precision?, options?)
float() // 0.814814
float(3) // 0.814range(min, max, options?)
range(1, 100) // 14array(count, digits?, options?)
array(3, 2) // [14, 41, 68] (sub-seed per element: seed + i × (2 × digitSum + 1))uuid(options?)
uuid() // deterministic UUID v4 formatoddOrEven(options?)
oddOrEven() // "even"redOrBlack(options?)
redOrBlack() // "red"resolveSeed(seed?)
Returns the numeric seed that any input resolves to. Handy for showing what a text seed became.
resolveSeed('brian') // 814
resolveSeed(-42.9) // 42randomSeed()
Generate a random seed using crypto.getRandomValues (with Math.random fallback).
randomSeed() // e.g. 3847291056 (different each call)Simulation Functions
coin(options?)
Deterministic coin flip.
coin() // "tails"dice(sides?, options?)
Deterministic die result.
dice() // 4 (6-sided)
dice(20) // 14card(options?)
Deterministic playing card draw.
card() // "8 of Diamonds"rps(options?)
Deterministic rock-paper-scissors.
rps() // "scissors"magic8(options?)
Deterministic 8-ball response.
magic8() // "Reply hazy, try again."zodiac(options?)
Deterministic zodiac sign.
zodiac() // "Gemini"tarot(options?)
Deterministic tarot card.
tarot() // "The Magician"fortune(options?)
Deterministic fortune message.
fortune() // "The answer you seek was never in doubt."spin(array, options?)
Deterministic selection from an array.
spin(['a', 'b', 'c', 'd']) // "d"roll(notation, options?)
Deterministic dice notation result (tabletop RPG style).
roll('2d6+3') // { rolls: [5, 2], modifier: 3, total: 10 }bingo(options?)
Deterministic bingo call.
bingo() // "B-14"color(options?)
Deterministic hex color.
color() // "#a81414"roulette(options?)
Deterministic number with color and parity.
roulette() // { number: 14, color: "red", parity: "even" }Custom Seeds
Every function accepts an options object with a seed property:
import { coin, dice, card } from 'pdrng';
coin({ seed: 42 }) // "heads"
dice(6, { seed: 42 }) // 4
card({ seed: 'brian' }) // deterministic card from text seedText seeds are converted using a rolling XOR hash, designed so that "brian" maps to seed 814:
pdrng(3, { seed: 'brian' }) // 814 (same as default!)
pdrng(4, { seed: 'brian' }) // 8148Random Seeds
While pdrng is deterministic by design, you can use random input to get non-deterministic behavior. Pass Math.random(), crypto.getRandomValues(), or use the built-in randomSeed() helper:
import { coin, dice, randomSeed } from 'pdrng';
// Built-in helper (uses crypto.getRandomValues for real entropy)
coin({ seed: randomSeed() }) // different each call
// Math.random() works directly as a seed
dice(6, { seed: Math.random() }) // different each callImports
// Default import (with all methods attached)
import pdrng from 'pdrng';
pdrng.coin();
// Named imports
import { coin, dice, card, roll } from 'pdrng';
coin();Validation
Numeric arguments are validated and throw RangeError on bad input, never NaN:
dice(0) // RangeError: sides must be an integer >= 1, received 0
range(10, 1) // RangeError: max must be an integer between 10 and ...
pdrng(20) // RangeError: digits must be an integer between 1 and 15, received 20Frozen Outputs
Outputs for a given seed are a contract. test/snapshot.test.js locks every function's result for seed 814, the text seed "brian", and several others. Any change to those values is a breaking change and ships as a new major version.
Distributions are deliberately uneven: results come from the seed's digits, not from a uniform generator. Text seeds always hash to an even number, so oddOrEven is "even" for every word. That is by design.
Website and API
The web/ folder contains the React site and the Netlify Functions REST API. Every endpoint is GET, accepts ?seed=, and returns JSON:
/api/v1/profile?seed=brian everything at once
/api/v1/dice?seed=brian&sides=20 14
/api/v1/roll?seed=brian¬ation=2d6+3
/api/v1/spin?seed=brian&choices=pizza,tacos,sushi
/api/openapi.json OpenAPI 3.1 spec
/api/docs Swagger UIDeterministic routes are cached immutably at the edge because the same input always gives the same output.
Requirements
- Node.js 20+
- ESM only (
import/export) - TypeScript definitions included
License
MIT
