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

pcg64dxsm

v2.0.0

Published

Fast, bit-compatible PCG64-DXSM pseudo-random number generator for JavaScript. WASM-accelerated, NumPy-compatible, works in Node.js and browsers.

Readme

PCG64DXSM.js

A high-quality, deterministic, fast pseudo-random number generator (PRNG) for JavaScript, based on PCG64-DXSM. Version 2 is a complete rewrite with a WebAssembly core — dramatically faster than the original BigInt implementation while keeping a drop-in API and bit-for-bit output.

PCG (Permuted Congruential Generator) is a family of modern PRNGs with strong statistical properties, compact state, and excellent equidistribution compared to older generators like XorShift64* or Mersenne Twister. This package is bit-compatible with NumPy's PCG64DXSM bit generator, and works in Node.js (ESM and CJS) and all modern browsers.


✨ Features

  • PCG64-DXSM core — 128-bit state, 128-bit increment, full period 2^128, independent streams via inc.
  • WebAssembly-accelerated — ~16× faster than pure-BigInt JavaScript on a realistic mixed workload.
  • NumPy-compatible output — same state/inc produces the same sequence as numpy.random.PCG64DXSM.
  • Advance & seek — jump forward/backward by any offset, or seek to exact logical position.
  • Deterministic & replayable — serialize and restore full generator state.
  • Automatic memory management — WASM-side state is released by FinalizationRegistry when the JS wrapper is garbage-collected.
  • Universal distribution — ESM, CommonJS, and browser IIFE builds in one package, plus TypeScript definitions.
  • Zero runtime dependencies.
  • Convenient object API (all methods on the engine instance):
    • random() / nextFloat64() — float in [0, 1).
    • nextUint64() — raw 64-bit BigInt.
    • integer(min, max) — inclusive integer range.
    • real(min, max, inclusive) — float range.
    • intBelow(n)[0, n) with unbiased Lemire rejection.
    • bool() / bool(pct) / bool(num, den).
    • pick(array[, begin[, end]]), sample(array, k).
    • shuffle(array)27× faster on Uint32Array than on plain arrays.
    • die(sides), dice(sides, count).
    • uuid4(), string(len) / string(pool, len), hex(len), date(start, end).
    • advance(delta), seek(pos), pos(), reset(), jumped(k), clone().

🚀 Installation

npm install pcg64dxsm

🖥 Usage (Node.js, ESM — recommended)

import PCG64DXSM from 'pcg64dxsm';

const rng = new PCG64DXSM();              // auto-seed via crypto.getRandomValues
console.log(rng.random());                 // like Math.random()
console.log(rng.integer(1, 6));            // dice roll
console.log(rng.uuid4());                  // random UUID v4

🖥 Usage (Node.js, CommonJS)

const PCG64DXSM = require('pcg64dxsm');

const rng = new PCG64DXSM();
console.log(rng.random());

The WASM module is automatically loaded when you import/require the package; no await init(...) is needed in Node.

🌐 Usage (Browser — native ESM)

Modern browsers support ES modules directly. Point your import at the package and initialize the WASM once:

<script type="module">
  import PCG64DXSM from './node_modules/pcg64dxsm/pcg64dxsm.js';
  // or from a CDN: https://cdn.jsdelivr.net/npm/pcg64dxsm/pcg64dxsm.js

  // One-time WASM load — returns when ready.
  await PCG64DXSM.init(new URL('./node_modules/pcg64dxsm/pcg64dxsm.wasm', import.meta.url));

  const rng = new PCG64DXSM();
  console.log(rng.integer(1, 100));
</script>

🌐 Usage (Browser — legacy <script> tag)

For sites without a module setup, use the IIFE build which exposes window.PCG64DXSM:

<script src="pcg64dxsm.browser.js"></script>
<script>
  (async () => {
    await PCG64DXSM.init('./pcg64dxsm.wasm');
    const rng = new PCG64DXSM();
    console.log(rng.integer(1, 100));
  })();
</script>

⚡ Performance

Measured on Node 22, single core, compared against a pure-BigInt reference implementation of the same algorithm:

| Task | BigInt | This package (WASM) | Speedup | |---|---|---|---| | nextUint64 × 1,000,000 | 372 ms | 75 ms | 5.0× | | shuffle(Uint32Array[39]) × 100,000 | 1621 ms | 61 ms | 26.7× | | Monte-Carlo inner loop (50 draws × 1000 seeds, jumped + shuffle) | 400 seeds/sec | 6,556 seeds/sec | 16.4× |

Bit-for-bit validated against the NumPy PCG64DXSM bit generator and against the v1 pure-BigInt implementation.

Performance tip: use Uint32Array for shuffle

shuffle() on a typed array runs fully inside WASM, touching no JS objects. On a plain Array, it has to copy each element into and out of WASM memory:

// Slow-ish: ~7× faster than BigInt
const balls = [];
for (let i = 1; i <= 39; i++) balls.push(i);
rng.shuffle(balls);

// Fastest: ~27× faster than BigInt
const balls = new Uint32Array(39);
for (let i = 0; i < 39; i++) balls[i] = i + 1;
rng.shuffle(balls);

📦 API

Construction

new PCG64DXSM()                      // crypto entropy (32 bytes)
new PCG64DXSM(uint8array)            // 16 bytes = state (inc=1), 32 bytes = state + inc
new PCG64DXSM(state, inc)            // numbers, BigInts, or hex strings
PCG64DXSM.fromSeed({ state, inc })   // object form
PCG64DXSM.fromRandom()               // alias for new PCG64DXSM()

Core output

rng.random()         // float in [0, 1)   — Math.random replacement
rng.nextFloat64()    // same as random()
rng.nextUint64()     // BigInt in [0, 2^64)
rng.nextUint64Pair(out)  // writes [lo32, hi32] to out:Uint32Array(2) — no BigInt allocation

Helpers

rng.integer(min, max)                // inclusive integer
rng.real(min, max, inclusive)        // float
rng.intBelow(n)                      // unbiased [0, n)
rng.bool()                           // 50/50
rng.bool(75)                         // 75%
rng.bool(1, 6)                       // 1-in-6
rng.pick([...], begin, end)
rng.shuffle(array)                   // in-place, returns the array
rng.sample([...], k)
rng.die(6), rng.dice(6, 3)
rng.uuid4()
rng.string(16)
rng.string('abc123', 8)
rng.hex(32, true)                    // 32 uppercase hex chars
rng.date(new Date(2020,0,1), new Date())

Positioning

rng.getState()                       // { state, inc, counter } — all strings
rng.setState({ state, inc, counter })
rng.clone()                          // independent copy with its own WASM state
rng.advance(n)                       // forward (n>0) or backward (n<0); number or BigInt
rng.seek(pos)                        // absolute position from original seed
rng.pos()                            // current logical position
rng.reset()                          // seek(0)
rng.jumped(k)                        // advanced by k * JUMP_DISTANCE (see note)

Lifecycle

rng.destroy()   // optional: release WASM state immediately (32 bytes)
                // Otherwise FinalizationRegistry handles it when the wrapper is GC'd.

📖 Examples

Reproducible sequences

import PCG64DXSM from 'pcg64dxsm';

const rng = new PCG64DXSM('0x1234567890ABCDEF', '0xCAFEBABE');
const dice = [];
for (let i = 0; i < 5; i++) dice.push(rng.integer(1, 6));
// → same five values every run

rng.reset();
// ...will now reproduce the exact same sequence.

Rewind and replay

const rng = new PCG64DXSM();

const a = rng.integer(1, 6);
const b = rng.integer(1, 6);

rng.advance(-1);               // rewind one draw
const b2 = rng.integer(1, 6);  // === b

rng.seek(0);                   // back to the very start
const a2 = rng.integer(1, 6);  // === a

Independent streams via jumped

Useful for Monte Carlo: jump by enormous multiples of JUMP_DISTANCE to get streams that will never overlap within any realistic simulation.

const master = new PCG64DXSM(seedBytes);
for (let i = 0; i < 1000; i++) {
  const worker = master.jumped(i).clone();   // independent stream
  // ...use worker
}

Note on jumped() memory: for performance, rng.jumped(k) returns a transient RNG backed by a scratch slot owned by the parent. It's valid until the next call to jumped() on the same parent. If you need it to persist beyond that, call .clone() on the returned RNG (as above). The common pattern rng.jumped(k).shuffle(arr) — use immediately, then discard — is fully supported.

Cross-check with NumPy

const rng = new PCG64DXSM(0x1234n, 0xABCDn);
console.log(rng.nextUint64().toString(16));
import numpy as np
bg = np.random.PCG64DXSM(0)
st = bg.state
st['state']['state'] = 0x1234
st['state']['inc']   = 0xABCD | 1   # inc must be odd
# also perform the same canonical seeding step: state = (state + inc) * MUL + inc
MUL = 0xDA942042E4DD58B5
st['state']['state'] = ((0x1234 + (0xABCD | 1)) * MUL + (0xABCD | 1)) & ((1 << 128) - 1)
bg.state = st
print(hex(bg.random_raw()))   # matches JS output

🎛 Seeding & Entropy

  • Automaticnew PCG64DXSM() pulls 32 bytes from crypto.getRandomValues (or Node's crypto as fallback).
  • Bytesnew PCG64DXSM(u8) where u8 is Uint8Array(16) (state only; inc = 1) or Uint8Array(32) (state + inc).
  • Explicit pairnew PCG64DXSM(state, inc) where each is a number, BigInt, or hex string like '0xDEADBEEF'.
  • From objectPCG64DXSM.fromSeed({ state, inc }).

What are state and inc?

  • state is the internal LCG state — controls where in the sequence you start.
  • inc is the stream selector — must be odd. Different inc values produce independent streams, each with period 2^128.

PCG64DXSM is not a cryptographic PRNG. For keys, tokens, or any security-sensitive output, use crypto.getRandomValues or Node's crypto directly.


⚖️ vs. alternatives

  • vs. Math.random() — deterministic, reproducible, much better statistical quality, same drop-in random() method.
  • vs. XorShift64★ — 128-bit state (vs 64), independent streams, stronger low-bit randomness, built-in jump/seek.
  • vs. Mersenne Twister (MT19937) — smaller state (16 bytes vs ~2.5 KB), faster per draw, no high-dimensional lattice issues, and now the NumPy default.
  • vs. pure-BigInt PCG64DXSM (v1 of this package) — identical output, 5–27× faster, lower allocation pressure.
  • vs. cryptographic RNG — not a CSPRNG. Use crypto.getRandomValues/Node crypto for secrets.

❌ Limitations

  • Not cryptographically secure.
  • Requires a runtime that supports WebAssembly + BigInt + FinalizationRegistry — i.e., Node ≥ 18 and all evergreen browsers since 2021.
  • The .wasm file (≈5 KB) must be reachable from the JS file; bundlers may need a loader rule for .wasm assets if you aren't using the default resolution.

🛠 Migrating from v1.x

v2 keeps the public API identical. If you used v1:

  • No changes needed to rng.nextUint64(), rng.random(), rng.shuffle(), rng.jumped(), etc. — they return and behave the same.
  • rng.pos() still returns number when safe, BigInt otherwise.
  • For maximum speed, change shuffled arrays to Uint32Array where feasible.
  • If you were using v1 in a Web Worker or browser via <script>, either switch to ESM (pcg64dxsm.js) or use the new pcg64dxsm.browser.js IIFE build and call await PCG64DXSM.init('./pcg64dxsm.wasm') once.

📜 License

MIT


📚 References