Gathering detailed insights and metrics for pure-rand
Gathering detailed insights and metrics for pure-rand
Gathering detailed insights and metrics for pure-rand
Gathering detailed insights and metrics for pure-rand
npm install pure-rand
Typescript
Module System
Node Version
NPM Version
TypeScript (88.08%)
JavaScript (11.92%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
88 Stars
772 Commits
4 Forks
2 Watchers
11 Branches
1 Contributors
Updated on Jun 26, 2025
Latest Version
7.0.1
Package Id
pure-rand@7.0.1
Unpacked Size
83.24 kB
Size
18.27 kB
File Count
93
NPM Version
10.9.2
Node Version
18.20.6
Published on
Feb 07, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
Fast Pseudorandom number generators (aka PRNG) with purity in mind!
Install it in node via:
npm install pure-rand
or yarn add pure-rand
Use it in browser by doing:
import * as prand from 'https://unpkg.com/pure-rand/lib/esm/pure-rand.js';
Simple usage
1import prand from 'pure-rand'; 2 3const seed = 42; 4const rng = prand.xoroshiro128plus(seed); 5const firstDiceValue = prand.unsafeUniformIntDistribution(1, 6, rng); // value in {1..6}, here: 2 6const secondDiceValue = prand.unsafeUniformIntDistribution(1, 6, rng); // value in {1..6}, here: 4 7const thirdDiceValue = prand.unsafeUniformIntDistribution(1, 6, rng); // value in {1..6}, here: 6
Pure usage
Pure means that the instance rng
will never be altered in-place. It can be called again and again and it will always return the same value. But it will also return the next rng
. Here is an example showing how the code above can be translated into its pure version:
1import prand from 'pure-rand'; 2 3const seed = 42; 4const rng1 = prand.xoroshiro128plus(seed); 5const [firstDiceValue, rng2] = prand.uniformIntDistribution(1, 6, rng1); // value in {1..6}, here: 2 6const [secondDiceValue, rng3] = prand.uniformIntDistribution(1, 6, rng2); // value in {1..6}, here: 4 7const [thirdDiceValue, rng4] = prand.uniformIntDistribution(1, 6, rng3); // value in {1..6}, here: 6 8 9// You can call: prand.uniformIntDistribution(1, 6, rng1); 10// over and over it will always give you back the same value along with a new rng (always producing the same values too).
Independent simulations
In order to produce independent simulations it can be tempting to instanciate several PRNG based on totally different seeds. While it would produce distinct set of values, the best way to ensure fully unrelated sequences is rather to use jumps. Jump just consists into moving far away from the current position in the generator (eg.: jumping in Xoroshiro 128+ will move you 264 generations away from the current one on a generator having a sequence of 2128 elements).
1import prand from 'pure-rand'; 2 3const seed = 42; 4const rngSimulation1 = prand.xoroshiro128plus(seed); 5const rngSimulation2 = rngSimulation1.jump(); // not in-place, creates a new instance 6const rngSimulation3 = rngSimulation2.jump(); // not in-place, creates a new instance 7 8const diceSim1Value = prand.unsafeUniformIntDistribution(1, 6, rngSimulation1); // value in {1..6}, here: 2 9const diceSim2Value = prand.unsafeUniformIntDistribution(1, 6, rngSimulation2); // value in {1..6}, here: 5 10const diceSim3Value = prand.unsafeUniformIntDistribution(1, 6, rngSimulation3); // value in {1..6}, here: 6
Non-uniform usage
While not recommended as non-uniform distribution implies that one or several values from the range will be more likely than others, it might be tempting for people wanting to maximize the throughput.
1import prand from 'pure-rand'; 2 3const seed = 42; 4const rng = prand.xoroshiro128plus(seed); 5const rand = (min, max) => { 6 const out = (rng.unsafeNext() >>> 0) / 0x100000000; 7 return min + Math.floor(out * (max - min + 1)); 8}; 9const firstDiceValue = rand(1, 6); // value in {1..6}, here: 6
Select your seed
While not perfect, here is a rather simple way to generate a seed for your PNRG.
1const seed = Date.now() ^ (Math.random() * 0x100000000);
In computer science most random number generators(1) are pseudorandom number generators (abbreviated: PRNG). In other words, they are fully deterministic and given the original seed one can rebuild the whole sequence.
Each PRNG algorithm has to deal with tradeoffs in terms of randomness quality, speed, length of the sequence(2)... In other words, it's important to compare relative speed of libraries with that in mind. Indeed, a Mersenne Twister PRNG will not have the same strenghts and weaknesses as a Xoroshiro PRNG, so depending on what you need exactly you might prefer one PRNG over another even if it will be slower.
4 PRNGs come with pure-rand:
congruential32
: Linear Congruential generator — [more]mersenne
: Mersenne Twister generator — [more]xorshift128plus
: Xorshift 128+ generator — [more]xoroshiro128plus
: Xoroshiro 128+ generator — [more]Our recommendation is xoroshiro128plus
. But if you want to use another one, you can replace it by any other PRNG provided by pure-rand in the examples above.
Once you are able to generate random values, next step is to scale them into the range you want. Indeed, you probably don't want a floating point value between 0 (included) and 1 (excluded) but rather an integer value between 1 and 6 if you emulate a dice or any other range based on your needs.
At this point, simple way would be to do min + floor(random() * (max - min + 1))
but actually it will not generate the values with equal probabilities even if you use the best PRNG in the world to back random()
. In order to have equal probabilities you need to rely on uniform distributions(3) which comes built-in in some PNRG libraries.
pure-rand provides 3 built-in functions for uniform distributions of values:
uniformIntDistribution(min, max, rng)
uniformBigIntDistribution(min, max, rng)
- with min
and max
being bigint
uniformArrayIntDistribution(min, max, rng)
- with min
and max
being instances of ArrayInt = {sign, data}
ie. sign either 1 or -1 and data an array of numbers between 0 (included) and 0xffffffff (included)And their unsafe equivalents to change the PRNG in-place.
Some helpers are also provided in order to ease the use of RandomGenerator
instances:
prand.generateN(rng: RandomGenerator, num: number): [number[], RandomGenerator]
: generates num
random values using rng
and return the next RandomGenerator
prand.skipN(rng: RandomGenerator, num: number): RandomGenerator
: skips num
random values and return the next RandomGenerator
The chart has been split into three sections:
Math.random()
In order to compare the performance of the libraries, we aked them to shuffle an array containing 1,000,000 items (see code).
We then split the measurements into two sections:
The recommended setup for pure-rand is to rely on our Xoroshiro128+. It provides a long enough sequence of random values, has built-in support for jump, is really efficient while providing a very good quality of randomness.
Non-Uniform
Library | Algorithm | Mean time (ms) | Compared to pure-rand |
---|---|---|---|
native (node 16.19.1) | Xorshift128+ | 33.3 | 1.4x slower |
pure-rand @6.0.0 | Xoroshiro128+ | 24.5 | reference |
pure-rand @6.0.0 | Xorshift128+ | 25.0 | similar |
pure-rand @6.0.0 | Mersenne Twister | 30.8 | 1.3x slower |
pure-rand @6.0.0 | Congruential | 22.6 | 1.1x faster |
seedrandom @3.0.5 | Alea | 28.1 | 1.1x slower |
seedrandom @3.0.5 | Xorshift128 | 28.8 | 1.2x slower |
seedrandom @3.0.5 | Tyche-i | 28.6 | 1.2x slower |
seedrandom @3.0.5 | Xorwow | 32.0 | 1.3x slower |
seedrandom @3.0.5 | Xor4096 | 32.2 | 1.3x slower |
seedrandom @3.0.5 | Xorshift7 | 33.5 | 1.4x slower |
@faker-js/faker @7.6.0 | Mersenne Twister | 109.1 | 4.5x slower |
chance @1.1.10 | Mersenne Twister | 142.9 | 5.8x slower |
Uniform
Library | Algorithm | Mean time (ms) | Compared to pure-rand |
---|---|---|---|
pure-rand @6.0.0 | Xoroshiro128+ | 53.5 | reference |
pure-rand @6.0.0 | Xorshift128+ | 52.2 | similar |
pure-rand @6.0.0 | Mersenne Twister | 61.6 | 1.2x slower |
pure-rand @6.0.0 | Congruential | 57.6 | 1.1x slower |
random-js @2.1.0 | Mersenne Twister | 119.6 | 2.2x slower |
System details:
- OS: Linux 5.15 Ubuntu 22.04.2 LTS 22.04.2 LTS (Jammy Jellyfish)
- CPU: (2) x64 Intel(R) Xeon(R) Platinum 8272CL CPU @ 2.60GHz
- Memory: 5.88 GB / 6.78 GB
- Container: Yes
- Node: 16.19.1 - /opt/hostedtoolcache/node/16.19.1/x64/bin/node
Executed on default runners provided by GitHub Actions
(1) — Not all as there are also hardware-based random number generator.
(2) — How long it takes to reapeat itself?
(3) — While most users don't really think of it, uniform distribution is key! Without it entries might be biased towards some values and make some others less probable. The naive rand() % numValues
is a good example of biased version as if rand()
is uniform in 0, 1, 2
and numValues
is 2
, the probabilities are: P(0) = 67%
, P(1) = 33%
causing 1
to be less probable than 0
The following snippet is responsible for generating 32-bit floating point numbers that spread uniformly between 0 (included) and 1 (excluded).
1import prand from 'pure-rand'; 2 3function generateFloat32(rng) { 4 const g1 = prand.unsafeUniformIntDistribution(0, (1 << 24) - 1, rng); 5 const value = g1 / (1 << 24); 6 return value; 7} 8 9const seed = 42; 10const rng = prand.xoroshiro128plus(seed); 11 12const float32Bits1 = generateFloat32(rng); 13const float32Bits2 = generateFloat32(rng);
The following snippet is responsible for generating 64-bit floating point numbers that spread uniformly between 0 (included) and 1 (excluded).
1import prand from 'pure-rand'; 2 3function generateFloat64(rng) { 4 const g1 = prand.unsafeUniformIntDistribution(0, (1 << 26) - 1, rng); 5 const g2 = prand.unsafeUniformIntDistribution(0, (1 << 27) - 1, rng); 6 const value = (g1 * Math.pow(2, 27) + g2) * Math.pow(2, -53); 7 return value; 8} 9 10const seed = 42; 11const rng = prand.xoroshiro128plus(seed); 12 13const float64Bits1 = generateFloat64(rng); 14const float64Bits2 = generateFloat64(rng);
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
project is fuzzed
Details
Reason
packaging workflow detected
Details
Reason
6 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 5
Reason
Found 0/9 approved changesets -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
security policy file not detected
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
10 existing vulnerabilities detected
Details
Score
Last Scanned on 2025-07-07
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More