@beastjs/use-rng
v0.1.25
Published
A deterministic random number generator with Octane hook and standalone APIs
Maintainers
Readme
@beastjs/use-rng
Deterministic random-number generation for Octane applications and standalone TypeScript. Seed pairs are hashed with SHA-512, and generated seeds use Web Crypto.
Features
- Reproducible output from a client seed, server seed, and nonce
- Octane hook with compiler-independent hook slots
- Standalone manager with concurrency-safe nonce reservation
- Unbiased inclusive integer generation for dice, lotteries, and selections
- Rounded floating-point ranges with configurable precision
- ESM, CommonJS, and TypeScript declarations
Installation
For the Octane hook:
bun add @beastjs/use-rng octaneFor the standalone entry, octane is not required:
bun add @beastjs/use-rngThe package requires Node.js 20 or newer. Browser usage requires the Web Crypto API.
Octane hook
import { useRNG } from '@beastjs/use-rng'
export function DiceRoller() {
const { result, nonce, generateSeeds, rollInt } = useRNG()
return (
<section>
<button onClick={generateSeeds}>New seeds</button>
<button onClick={() => rollInt({ range: [1, 6] })}>Roll d6</button>
<p>Result: {result ?? '—'}</p>
<p>Next nonce: {nonce}</p>
</section>
)
}rollDice, rollInt, and setSeedPair return their generated value, so they can also be awaited directly:
const percentage = await rollDice({
range: [0, 100],
decimalPlaces: 2
})The hook uses its current seeds and nonce by default. Pass seedPair to reproduce an exact external input:
const value = await rollInt({
seedPair: {
clientSeed: 'player-42',
serverSeed: 'round-7',
nonce: 3
},
range: [1, 20]
})Hook API
useRNG() returns:
clientSeed,serverSeed,nonce— current deterministic inputresult— last rounded or integer resultseedPairNumber— last normalized seed-pair resultgenerateSeeds()— creates new 128-bit seeds and clears prior resultsrollDice(options?)— generates a rounded number and reserves a noncerollInt(options?)— generates an unbiased inclusive integer and reserves a noncesetSeedPair()— evaluates the current seed pair as a normalized numbergetCurrentSeedPair()— returns the latest seed values, including updates made in the current eventsetClientSeed,setServerSeed,setNonce— manual state setters
Options:
interface RandomWithPrecise {
seedPair?: SeedPair
range?: readonly [min: number, max: number]
decimalPlaces?: number
}
interface RandomIntegerOptions {
seedPair?: SeedPair
range?: readonly [min: number, max: number]
}Standalone API
import { RNGManager } from '@beastjs/use-rng/standalone'
const rng = new RNGManager('player-42', 'round-7')
const d6 = await rng.rollInt(1, 6)
const percentage = await rng.roll(0, 100, 2)
const nextD20 = await rng.peekInt(1, 20)
console.log({ d6, percentage, nextD20, seedPair: rng.getSeedPair() })RNGManager
new RNGManager(clientSeed?, serverSeed?, nonce?)roll(min?, max?, decimalPlaces?)— rounded result; increments the noncepeek(min?, max?, decimalPlaces?)— rounded result; leaves the nonce unchangedrollInt(min, max)— unbiased inclusive integer; increments the noncepeekInt(min, max)— unbiased inclusive integer; leaves the nonce unchangedgenerateSeeds()— replaces both seeds and resets the noncegetSeedPair()— returns a snapshot of the current seed pairsetSeeds(clientSeed, serverSeed, nonce?)— replaces the seed pairincrementNonce()/resetNonce()— manually manages the nonce
Concurrent roll() and rollInt() calls reserve their nonces before hashing, so a Promise.all batch produces the same ordered sequence as sequential calls.
Direct functions
Use the standalone entry when no UI framework is needed:
import {
getRandomInt,
getRandomSeedPair,
getRandomWithPrecision,
type SeedPair
} from '@beastjs/use-rng/standalone'
const seedPair: SeedPair = {
clientSeed: 'client',
serverSeed: 'server',
nonce: 0
}
const normalized = await getRandomSeedPair(seedPair)
const d20 = await getRandomInt(seedPair, 1, 20)
const amount = await getRandomWithPrecision(seedPair, 10, 20, 3)Also exported are hashSeed, bytesToInt, getRandomSeed, getRandomInRange, getRandomRange, and generateId.
Determinism and security
The same seed pair always produces the same output. Changing the nonce creates a reproducible sequence. getRandomInt uses rejection sampling over SHA-512 output to avoid modulo bias.
This library is suitable for simulations, games, deterministic assignments, and independently reproducible draws. It is not a replacement for Web Crypto when generating keys, passwords, session tokens, or other security credentials.
Development
bun install
bun run checkbun run check type-checks the library, runs the test suite, and builds both package entries.
License
MIT
