@hyperfrontend/ random-generator-utils§
Statistical random distributions and UUID generation for simulations, testing, and procedural content.
What is @hyperfrontend/random-generator-utils?
@hyperfrontend/random-generator-utils provides random number generators beyond JavaScript's basic Math.random(), focusing on statistical distributions used in simulations, load testing, and procedural generation. It includes Gaussian (normal), exponential, power law, and logarithmic distributions, plus UUID v4 generation and a seeded generator that replays every one of them from a single number.
Unlike cryptographic random generators (like Web Crypto API), these utilities prioritize reproducibility and distribution shapes over security. createRandomGenerator(seed) turns one number into a deterministic stream of every distribution for tests and procedural scenes, while the same distributions model real-world phenomena like response times, user behavior, and natural variation.
Key Features
Gaussian, exponential, power law, logarithmic, uniform
createRandomGenerator(seed)replays every distribution and UUID from one seedevery distribution accepts a
() => numbersource, so any generator can drive itwith validation (
uuidV4(),isUuidV4())(
randomPseudo()) for one-off reproducible valuesfor pseudo-random variations
No third-party dependencies
at runtime it imports only JavaScript built-ins and
@hyperfrontendutilitiesPure functions
for functional composition
Why Use @hyperfrontend/random-generator-utils?
Realistic Load Testing and Simulations
Math.random() generates uniform distributions, but real-world events follow different patterns. User response times cluster around an average (Gaussian), server failures often show exponential decay, and popularity follows power law distributions (80/20 rule). These generators let you model realistic scenarios in load tests and simulations.
Reproducible Pseudo-Random Sequences for Testing
createRandomGenerator(seed) returns a stream whose every method (uniform, gaussian, exponential, powerLaw, logarithmic, uuidV4) replays exactly for the same seed. Log the seed when a property test fails and pass it back in to reproduce the input, or derive it from a record id so every visitor sees the same procedural scene. For a single reproducible value with no stream to carry, randomPseudo(seed) hashes a number straight to a result, and randomPseudoTimeBased() does the same for a date, which gives daily or hourly variations that stay stable within their window.
UUID Generation Without External Dependencies
Many projects pull in the uuid package (500KB+) just for v4 UUIDs. This library provides a lightweight alternative with both generation and validation. Ideal for test fixtures, trace IDs, or non-security-critical unique identifiers without bloating bundles.
Functional Composition for Data Pipelines
All generators are pure functions accepting parameters and returning numbers. This makes them composable in data generation pipelines, Array methods (Array.from({ length: 100 }, () => randomGaussian(0, 100))), or streaming data generators for charts and visualizations.
Installation
npm install @hyperfrontend/random-generator-utils
Quick Start
import {
createRandomGenerator,
randomGaussian,
randomExponential,
randomPowerLaw,
randomUniform,
randomPseudo,
uuidV4,
isUuidV4,
} from '@hyperfrontend/random-generator-utils'
// Gaussian (normal) distribution - ideal for modeling natural variation
const responseTime = randomGaussian(100, 300) // ms, centered around 200ms
const userHeight = randomGaussian(160, 180) // cm, most values near 170cm
// Exponential distribution - models time between independent events
const timeBetweenRequests = randomExponential(0.5) // λ=0.5, mean=2 seconds
const failureRate = randomExponential(0.1) // λ=0.1, mean=10 units
// Power law distribution - models "rich get richer" phenomena
const popularity = randomPowerLaw(2, 1, 1000) // Few items very popular
const citySize = randomPowerLaw(1.1, 100, 1000000) // Zipf's law for cities
// Uniform distribution - flat probability across range
const randomDelay = randomUniform(0, 1000) // Any value 0-1000ms equally likely
// Seeded stream - every distribution replays from one number
const stream = createRandomGenerator(2026)
const size = stream.gaussian(24, 96) // Same value on every run that seeds 2026
const gap = stream.exponential(0.5) // ...and the next draw, and the next
const fixtureId = stream.uuidV4() // Stable ids for snapshot fixtures
// Any distribution can draw from the stream directly
const angle = randomUniform(0, 360, stream.next)
// Stateless seeded hash for a one-off reproducible value
const seed = 42
const value1 = randomPseudo(seed) // Always same output for seed=42
const value2 = randomPseudo(seed) // Identical to value1
// UUID generation
const id = uuidV4() // "a3bb189e-8bf9-4558-9e3e-e7b9a9e7b8c1"
console.log(isUuidV4(id)) // true
console.log(isUuidV4('not-a-uuid')) // false
API Overview
Five distributions, one call shape: parameters that describe the shape go in, a single number comes out.
randomGaussian(min, max) clusters draws around the midpoint of a
bounded range and never leaves it, randomExponential(lambda)
decays with a mean of 1 / lambda, and randomPowerLaw(alpha, min, max)
piles most of its mass near min while keeping a long tail out to max; randomLogarithmic and randomUniform cover the skewed and the flat cases. Every one
of them ends with an optional source: () => number that defaults to Math.random, and that last parameter is the seam the rest of the package plugs into.
createRandomGenerator(seed) fills the seam. It returns a
frozen object carrying the seed it was opened with, a next() that draws the stream's unit values, and one method per distribution, so a whole procedural
scene or fixture set becomes a function of one number and replays draw for draw on any machine. The methods share a single stream, which means the order of the
calls is part of what the seed reproduces. next is a plain function and detaches cleanly, so randomUniform(0, 360, stream.next) puts a free-standing
distribution on the same stream.
Two smaller pieces sit outside the stream. randomPseudo(seed) is a
stateless hash rather than a generator: one seed maps to one value forever, which is what you want for a single reproducible number and not what you want for a
sequence (randomPseudoTimeBased is the same hash over a Date, which is how you get a variation that holds steady for a day or an hour). And
uuidV4() generates a version 4 id, drawing from a seeded source when you
hand it one, with isUuidV4 to check a string coming back the other way.
Every parameter, bound and return type is in the API reference.
Use Cases
Load Testing
// Model realistic user behavior with varying response times
const users = Array.from({ length: 1000 }, () => ({
thinkTime: randomExponential(0.5), // Time between actions
responseTime: randomGaussian(50, 200), // Server response latency
requestCount: Math.floor(randomPowerLaw(2, 1, 100)), // Request frequency
}))
Test Data Generation
// Generate reproducible test datasets: log stream.seed, replay the run
const stream = createRandomGenerator(Date.now())
const testData = Array.from({ length: 50 }, () => ({
id: stream.uuidV4(),
score: stream.gaussian(0, 100),
timestamp: new Date(Date.now() + stream.uniform(0, 86400000)),
}))
Procedural Content
// Generate varied but natural-looking values
const terrain = {
height: randomGaussian(0, 100), // Centered around 50
vegetation: randomUniform(0, 1), // Uniform coverage
populationDensity: randomPowerLaw(2, 1, 1000), // Power law distribution
}
Compatibility
Runs on
- Node.js>=18.0.0supported
- Browserssupported
- Web Workerssupported
Ships as
- ESMTree-shakeable
- CJSNode and older bundlers
- IIFEHyperfrontendRandomGenerator
- UMDHyperfrontendRandomGenerator
CDN Usage
<!-- unpkg -->
<script src="https://unpkg.com/@hyperfrontend/random-generator-utils"></script>
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@hyperfrontend/random-generator-utils"></script>
<script>
const { randomGaussian, randomUniform, uuid4 } = HyperfrontendRandomGenerator
</script>
Global variable: HyperfrontendRandomGenerator
Architecture Highlights§
Every distribution is a mathematical transform over a unit draw. The draw comes from a source that defaults to Math.random() and can be any () => number; createRandomGenerator supplies a mulberry32 stream, a 32-bit generator with a period of 2^32 draws. Gaussian uses the polar form of the Box-Muller transform, exponential uses inverse transform sampling, and randomPseudo is a stateless sine hash.
API Reference§
ƒFunctions
The same seed gives the same values in the same order on every run and every machine, so a procedural scene, a fixture set, or a simulation becomes a function of one number. The methods share a single stream: interleave the calls differently and the values move, so keep the draw order stable wherever reproducibility matters.
Parameters
| Name | Type | Description |
|---|---|---|
§seed | number | Any finite number. Nearby, fractional, and negative seeds all open distinct streams. |
Returns
RandomGeneratornext is a plain function, safe to hand to any distribution as its source.Examples
The same scene on every load
const stream = createRandomGenerator(2026)
const trees = Array.from({ length: 40 }, () => ({
x: stream.uniform(0, 800),
height: stream.gaussian(60, 140),
}))
// => identical positions and heights on every run that seeds 2026Replaying a failing property test
const stream = createRandomGenerator(Date.now())
const input = stream.powerLaw(2, 1, 10000)
// Log stream.seed when the assertion fails, then pass it back in to reproduce the exact input.Seeding a free-standing distribution
const { next } = createRandomGenerator(7)
const wait = randomExponential(0.5, next)Parameters
| Name | Type | Description |
|---|---|---|
§str | string | the string to be validated. |
Returns
booleanExample
Validating user input as UUID
isUuidV4('a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d')
// => true
isUuidV4('not-a-uuid')
// => false
// Version 1 UUID (has '1' in third segment, not '4')
isUuidV4('550e8400-e29b-11d4-a716-446655440000')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§lambda | number | The rate parameter (λ) controlling the distribution shape; must be a positive finite number |
§source | RandomSource | Where the unit draw comes from; defaults to the built-in (default: Math.randomrandom) |
Returns
numberExamples
Modeling time between events (e.g., customer arrivals)
// Higher lambda = shorter average wait time
const averageWaitMinutes = 5
const lambda = 1 / averageWaitMinutes
const waitTime = randomExponential(lambda)
// => 3.7 (varies each call, most values clustered near 0-10)Replaying the same arrival gaps from a seed
const { next } = createRandomGenerator(7)
const gapSeconds = randomExponential(0.5, next)
// => the same gap on every run that seeds 7Parameters
| Name | Type | Description |
|---|---|---|
§min | number | The minimum value of the range |
§max | number | The maximum value of the range |
§source | RandomSource | Where the unit draws come from; defaults to the built-in (default: Math.randomrandom) |
Returns
numberExamples
Simulating human heights in centimeters
const heightCm = randomGaussian(150, 200)
// => 174.3 (most values cluster around the midpoint 175)Generating test scores with realistic distribution
const testScore = randomGaussian(0, 100)
// => 52.8 (bell curve centered at 50, rarely hits extremes)Drawing from a seeded stream instead of `Math.random`
const { next } = createRandomGenerator(7)
const size = randomGaussian(24, 96, next)
// => the same size on every run that seeds 7Parameters
| Name | Type | Description |
|---|---|---|
§scale | number | The scale parameter controlling the distribution spread |
§source | RandomSource | Where the unit draw comes from; defaults to the built-in (default: Math.randomrandom) |
Returns
numberExamples
Generating values with exponential growth characteristics
// scale=1 produces values from 1 to e (~2.718)
const smallScale = randomLogarithmic(1)
// => 1.8 (values between 1 and ~2.7)
// scale=5 produces values from 1 to e^5 (~148)
const largeScale = randomLogarithmic(5)
// => 42.3 (wider range, skewed toward lower values)Drawing from a seeded stream instead of `Math.random`
const { next } = createRandomGenerator(7)
const growth = randomLogarithmic(5, next)
// => the same value on every run that seeds 7Parameters
| Name | Type | Description |
|---|---|---|
§alpha | number | The standard Pareto/Zipf exponent: higher values concentrate more mass near min. Values between 1 and 3 are typical, alpha of 1 gives a log-uniform draw, and alpha of 0 gives a uniform draw. |
§min | number | The minimum value of the range; must be a finite number greater than zero |
§max | number | The maximum value of the range; must be a finite number greater than zero |
§source | RandomSource | Where the unit draw comes from; defaults to the built-in (default: Math.randomrandom) |
Returns
numberExamples
Simulating social network follower counts (few have many, many have few)
// alpha above 1 creates a long tail, so most values sit near min
const followerCount = randomPowerLaw(2.5, 1, 1000000)
// => 1.6 (typically low, occasionally very large)Modeling file sizes in a system
const fileSizeKb = randomPowerLaw(2.0, 1, 10000)
// => 2 (many small files, rare large files)Drawing from a seeded stream instead of `Math.random`
const { next } = createRandomGenerator(7)
const citySize = randomPowerLaw(1.1, 100, 1000000, next)
// => the same size on every run that seeds 7This is a stateless hash, not a stream: one seed maps to one value, and calling it twice with the same seed returns that value twice. To draw a reproducible sequence, or to seed the distributions, use
createRandomGenerator instead. When deriving several values from one base seed here, give every property its own offset and keep one item's seeds clear of the next item's, because two draws from the same number are the same number.Parameters
| Name | Type | Description |
|---|---|---|
§seed | number | The seed for the hash. |
Returns
numberExample
Reproducible random values for testing
// Same seed always yields the same result
randomPseudo(42)
// => 0.7845... (deterministic)
randomPseudo(42)
// => 0.7845... (identical)
randomPseudo(43)
// => 0.2525... (different seed, different result)Returns
numberExample
Reproducible randomness for a specific timestamp
const releaseDate = new Date('2024-03-15T10:30:00Z')
// Same date always produces the same result
const value1 = randomPseudoTimeBased(releaseDate)
const value2 = randomPseudoTimeBased(releaseDate)
// value1 === value2 (deterministic)Parameters
| Name | Type | Description |
|---|---|---|
§min | number | The minimum value of the range (inclusive) |
§max | number | The maximum value of the range (exclusive) |
§source | RandomSource | Where the unit draw comes from; defaults to the built-in (default: Math.randomrandom) |
Returns
numberExamples
Generating a random price within a budget range
const priceUsd = randomUniform(10, 50)
// => 27.34 (any value equally likely within range)Random coordinates for game object placement
const xPosition = randomUniform(0, 800)
const yPosition = randomUniform(0, 600)
// => x: 342.7, y: 198.2Drawing from a seeded stream instead of `Math.random`
const { next } = createRandomGenerator(7)
const startAngle = randomUniform(0, 360, next)
// => the same angle on every run that seeds 7Parameters
| Name | Type | Description |
|---|---|---|
§source | RandomSource | Where the unit draws come from; defaults to the built-in (default: Math.randomrandom) |
Returns
stringExamples
Creating unique identifiers for entities
const userId = uuidV4()
// => 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d'
const sessionId = uuidV4()
// => '9f8e7d6c-5b4a-4321-8765-4321fedcba98'Stable ids for snapshot fixtures
const { next } = createRandomGenerator(7)
const fixtureId = uuidV4(next)
// => the same id on every run that seeds 7◈Interfaces
Two generators created from the same seed return the same values in the same order, for every method, on every machine. All methods draw from the one stream, so the order of the calls is part of what the seed reproduces.
Properties
readonly next:RandomSourcereadonly seed:number◆Types
Every distribution in this package takes one as its last parameter and defaults to the built-in
Math.random. Pass a seeded source, such as the next of a RandomGenerator, and the draw becomes reproducible.type RandomSource = () => numberRelated reading§
- How-to
How to generate values that look natural instead of random
Everything I generate with Math.random comes out looking mechanical: sizes are spread evenly instead of clustering around a typical one, jitter is as likely to be huge as tiny, and spawned things arrive on a metronome no crowd ever moves to.
- Package
@hyperfrontend/cryptography
Used by @hyperfrontend/cryptography for secure random generation
- Getting started
Getting Started
Set HyperFrontend up and embed a first feature.
- Architecture
Architecture Guide
How the packages fit together.

