A functional implementation of the PCG family random number generators, written in TypeScript. Runs are reproducible, replayable, and rewindable. State can be serialized and reconstituted later. Ships dual ESM/CJS builds and TypeScript types. Runs on Node 18+, Bun, Deno, and modern browsers.
import { createPcg32, nextState, randomInt, randomList } from 'pcg'
// A stream id picks *which* unique periodic series of entropy to use.
// The seed picks *where* in that series to start.
const state0 = createPcg32({}, 42, 54)
const randomUint32 = randomInt(0, 2 ** 32)
const [value, state1] = randomUint32(state0)
const list = randomList(3, randomUint32, state0)
// list === [[v0, s0], [v1, s1], [v2, s2]] — value === v0, state1 === s0state0 is never mutated. Pass state1 into the next call to continue the stream, or hold onto state0 and replay it. Persist a state with JSON.stringify(state0) and rehydrate with JSON.parse.
createPcg32(options, seed, streamId) → PCGState
import { createPcg32, OutputFnType, StreamScheme } from 'pcg'
const state = createPcg32(
{
outputFnType: OutputFnType.XSH_RR,
streamScheme: StreamScheme.SETSEQ,
},
42,
54
)Output functions (OutputFnType) — different ways of permuting the 64-bit state into a 32-bit word.
| Variant | Notes |
|---|---|
XSH_RR (default) |
xor-shift high, random rotate. The PCG default; good all-rounder. |
XSH_RS |
xor-shift high, random shift. Slightly faster, marginally weaker. |
XSL_RR |
xor-shift low, random rotate. |
RXS_M_XS |
random xor-shift, multiply, xor-shift. Strongest of the four; slightly slower. |
Stream schemes (StreamScheme) — how the increment is chosen each step.
| Variant | Notes |
|---|---|
SETSEQ (default) |
Per-state stream id; lets independent streams coexist from one seed family. |
ONESEQ |
Single fixed increment; ignores streamId. |
MCG |
Multiplicative congruential generator (no increment). Fastest, but period halved. |
randomInt(min, max, state) → [number, PCGState]— uniform integer in[min, max). Curried soconst random = randomInt(min, max); random(state)also works.randomList(length, rng, state) → [value, PCGState][]— runsrnglengthtimes, threading the state. Also fully curried.getOutput(state) → number— output at the current state without advance.
nextState(state) → PCGState— advance by 1.prevState(state) → PCGState— rewind by 1. Unsupported by sfc32.stepState(delta, state) → PCGState— jump ahead or back where delta is a signed integer. O(log Δ) via Brown's jump-ahead algorithm for PCG.
PCGState, Uint64, RandomFn<T>, CreatePcgOptions, OutputFn, SchemeFn, PCGVariant are all exported. State is { state: Uint64, streamId: Uint64, variant, outputFnType, streamScheme } where Uint64 is { hi: number, lo: number }.
PCGState is no longer a bigint. The 64-bit halves are now { hi: number, lo:number } objects, so serialization with JSON.stringify round-trips cleanly.
See CHANGELOG.md for the full set of changes.
- @kripod, who wrote the original
pcg.js. - Melissa O'Neill, for her work on the PCG family.