Package Exports
- seedforge-prng
- seedforge-prng/dist/seedforge.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (seedforge-prng) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
🌱 SeedForge
Advanced Pseudo-Random Number Generator Library for JavaScript
A comprehensive, seedable PRNG library featuring multiple algorithms, statistical distributions, noise generators, and utilities designed for procedural generation, games, simulations, and reproducible randomness.
Table of Contents
- Features
- Installation
- Quick Start
- Algorithms
- API Reference
- Usage Examples
- Performance
- Browser Usage
- TypeScript Support
- Testing
- License
- Credits
Features
- 🎲 6 PRNG Algorithms - Mulberry32, Xoshiro128**, Xorshift128, PCG32, SFC32, LCG
- 📊 17 Statistical Distributions - Normal, Exponential, Poisson, Binomial, Gamma, Beta, Pareto, Triangular, Log-Normal, Weibull, Cauchy, Geometric, Zipf, Chi-Squared, Student's t, Von Mises, Hypergeometric
- 🌊 6 Noise Generators - Value, Simplex, Perlin, Worley (Cellular), Ridged, Billowed + fBm, turbulence, domain warping
- 📦 Array Utilities - Shuffle, pick, sample, weighted selection
- 📐 Geometric Utilities - Random points in/on circles, spheres, rectangles, boxes
- 💾 State Management - Save, restore, reset, clone, and fork generators
- 🔄 100% Reproducible - Same seed always produces identical sequences
- 📝 TypeScript Support - Full type definitions included
- 🚀 Zero Dependencies - Pure JavaScript, works everywhere
- 🌐 Universal - Works in Node.js, browsers, and module bundlers
Installation
npm
npm install seedforge-prngBrowser (CDN)
<script src="https://unpkg.com/seedforge-prng/dist/seedforge.js"></script>Manual Download
Download seedforge.js from the dist/ folder and include it in your project.
Quick Start
Node.js / CommonJS
const { PRNG } = require('seedforge-prng');
const rng = new PRNG('my-seed');
console.log(rng.random()); // 0.7364291045814753
console.log(rng.int(1, 100)); // 42ES Modules
import { PRNG } from 'seedforge-prng';
const rng = new PRNG('my-seed');
console.log(rng.random());Browser
<script src="seedforge.js"></script>
<script>
const rng = new PRNG.PRNG('my-seed');
console.log(rng.random());
</script>Basic Example
const { PRNG } = require('seedforge-prng');
// Create a seeded generator
const rng = new PRNG('my-game-seed');
// Generate various random values
rng.random(); // Float in [0, 1) → 0.7364291045814753
rng.int(1, 100); // Integer in [1, 100] → 42
rng.float(5.0, 10.0); // Float in [5, 10) → 7.284729103847
rng.bool(0.7); // 70% chance of true → true
rng.sign(); // -1 or 1 → -1
// Reproducibility: same seed = same sequence
const rng2 = new PRNG('my-game-seed');
rng2.random(); // 0.7364291045814753 (identical!)Algorithms
SeedForge includes 6 different PRNG algorithms. Each has different characteristics:
| Algorithm | Period | Speed | Quality | Best For |
|---|---|---|---|---|
mulberry32 |
~2³² | ★★★★★ | ★★★☆☆ | Simple games, quick prototypes |
xoshiro128 |
2¹²⁸-1 | ★★★★☆ | ★★★★★ | General purpose (default) |
xorshift128 |
2¹²⁸-1 | ★★★☆☆ | ★★★★☆ | When you need xorshift specifically |
pcg32 |
2⁶⁴ | ★★★★☆ | ★★★★★ | Simulations, statistics |
sfc32 |
~2¹²⁸ | ★★★★★ | ★★★★★ | Best all-around choice |
lcg |
2³² | ★★★★★ | ★★☆☆☆ | Legacy compatibility |
⚠️ Performance Note:
xorshift128may be slower than other algorithms in some JavaScript environments. For best performance, usesfc32,mulberry32, orxoshiro128.
Choosing an Algorithm
// Default (xoshiro128) - great for most uses
const rng = new PRNG('seed');
// Specify algorithm
const rng1 = new PRNG('seed', 'sfc32'); // Best all-around
const rng2 = new PRNG('seed', 'mulberry32'); // Fastest
const rng3 = new PRNG('seed', 'pcg32'); // Best for statisticsAlgorithm Details
Mulberry32 - Extremely fast, small state (32-bit). Good for games where speed matters more than statistical perfection.
Xoshiro128** - Excellent statistical properties with 128-bit state. Supports jump() for creating parallel streams. The default choice.
PCG32 - Permuted Congruential Generator. Exceptional statistical quality, passes all BigCrush tests. Best for Monte Carlo simulations.
SFC32 - Simple Fast Counter. Extremely fast while maintaining excellent quality. Passes PractRand. Recommended for most uses.
LCG - Linear Congruential Generator. Classic algorithm, included for compatibility. Lower quality but predictable behavior.
API Reference
Creating a Generator
const { PRNG } = require('seedforge-prng');
// With string seed (recommended)
const rng = new PRNG('my-seed');
// With number seed
const rng = new PRNG(12345);
// With specific algorithm
const rng = new PRNG('my-seed', 'pcg32');
// Change seed later
rng.setSeed('new-seed');
rng.setSeed('new-seed', 'sfc32'); // Also change algorithmBasic Generation
random() → number
Returns a random float in the range [0, 1).
rng.random(); // 0.7364291045814753
rng.random(); // 0.2847293018374628
rng.random(); // 0.9173648201847362randomInt() → number
Returns a random 32-bit unsigned integer.
rng.randomInt(); // 3162948573
rng.randomInt(); // 1847362910int(min, max) → number
Returns a random integer in the range [min, max] (inclusive).
rng.int(1, 6); // 4 (like rolling a die)
rng.int(0, 100); // 73
rng.int(-10, 10); // -3float(min, max) → number
Returns a random float in the range [min, max).
rng.float(0, 1); // 0.7364291045814753
rng.float(10, 20); // 15.847293018374628
rng.float(-5.5, 5.5); // 2.183746281037465bool(probability?) → boolean
Returns true with the given probability (default 0.5).
rng.bool(); // true (50% chance)
rng.bool(0.8); // true (80% chance)
rng.bool(0.1); // false (10% chance of true)sign(probability?) → -1 | 1
Returns -1 or 1, with the given probability of returning 1.
rng.sign(); // -1 or 1 (50/50)
rng.sign(0.7); // 1 (70% chance) or -1 (30% chance)Statistical Distributions
normal(mean?, stdDev?) → number
Normal (Gaussian) distribution using Box-Muller transform.
// Standard normal (mean=0, stdDev=1)
rng.normal(); // -0.284 to 2.847 (typically)
// IQ-like distribution
rng.normal(100, 15); // 94.28, 112.47, 87.19...
// Character stats
rng.normal(50, 10); // 48.2, 53.7, 41.9...Use cases: Character attributes, natural measurements, test scores, any "bell curve" data.
exponential(lambda?) → number
Exponential distribution for modeling time between events.
// Average of 2 (lambda = 0.5, mean = 1/lambda)
rng.exponential(0.5); // 1.847, 0.293, 3.182...
// Average of 10
rng.exponential(0.1); // 8.47, 12.93, 3.82...Use cases: Time between enemy spawns, item drops, random events.
poisson(lambda) → number
Poisson distribution for counting random events.
// Average 4 events
rng.poisson(4); // 3, 5, 4, 2, 6...
// Average 10 events
rng.poisson(10); // 8, 11, 10, 12, 9...Use cases: Number of enemies in an encounter, items in a chest, events per time period.
binomial(n, p) → number
Binomial distribution - number of successes in n trials.
// 10 coin flips
rng.binomial(10, 0.5); // 4, 6, 5, 7, 3...
// 20 attempts with 30% success rate
rng.binomial(20, 0.3); // 6, 5, 8, 4, 7...Use cases: Critical hit streaks, success counts, quality rolls.
triangular(min?, max?, mode?) → number
Triangular distribution with a peak at mode.
// Peak at 0.5 (symmetric)
rng.triangular(0, 1, 0.5); // 0.42, 0.58, 0.51...
// Peak near max (skewed right)
rng.triangular(0, 100, 80); // 72, 85, 68, 91...
// Peak near min (skewed left)
rng.triangular(1, 10, 2); // 2.4, 3.1, 1.8...Use cases: Task completion times, price variations, biased random values.
pareto(alpha?, xm?) → number
Pareto distribution (power law / 80-20 rule).
// Typical wealth distribution
rng.pareto(1.16, 1); // 1.2, 2.8, 1.1, 15.4, 1.3...
// Steeper falloff
rng.pareto(2, 10); // 11.2, 10.8, 14.7, 10.3...Use cases: Loot value, city sizes, wealth distribution, popularity.
gamma(shape, scale?) → number
Gamma distribution for waiting times and life spans.
rng.gamma(2, 2); // 3.182, 1.847, 4.293...
rng.gamma(5, 1); // 4.28, 5.91, 3.72...Use cases: Wait times, rainfall amounts, insurance claims.
beta(alpha, beta) → number
Beta distribution for probabilities and proportions (always 0-1).
// Uniform-ish
rng.beta(1, 1); // 0.28, 0.74, 0.51...
// Skewed toward 0
rng.beta(1, 5); // 0.12, 0.08, 0.23...
// Skewed toward 1
rng.beta(5, 1); // 0.87, 0.92, 0.78...
// Bell-shaped
rng.beta(5, 5); // 0.48, 0.52, 0.44...Use cases: AI confidence levels, completion percentages, probability parameters.
logNormal(mu?, sigma?) → number
Log-normal distribution for multiplicative processes.
rng.logNormal(0, 0.5); // 0.87, 1.42, 0.68...
rng.logNormal(0, 1); // 0.54, 2.18, 0.31...Use cases: Stock prices, file sizes, organism sizes.
weibull(scale?, shape?) → number
Weibull distribution for reliability/survival analysis.
rng.weibull(1, 1); // Exponential
rng.weibull(1, 2); // Rayleigh
rng.weibull(1, 3.6); // Approximately normalUse cases: Equipment lifetime, failure rates, wind speeds.
cauchy(location?, scale?) → number
Cauchy distribution with heavy tails (extreme outliers).
rng.cauchy(0, 1); // -2.38, 0.47, 15.82, -0.18...Use cases: When you want occasional extreme values, resonance phenomena.
geometric(p) → number (New in v1.1.0)
Geometric distribution - number of trials until first success.
// 30% success rate per trial
rng.geometric(0.3); // 1, 4, 2, 1, 7, 3...
// 50% success rate
rng.geometric(0.5); // 1, 2, 1, 1, 3, 1...
// 10% success rate (rare successes)
rng.geometric(0.1); // 8, 12, 5, 15, 3...Use cases: Number of attempts until success, retry counts, rare drop farming.
zipf(n, s?) → number (New in v1.1.0)
Zipf distribution - power-law ranking (1 is most common, n is rarest).
// Top 10 ranking (s=1 default)
rng.zipf(10); // 1, 1, 2, 1, 3, 1, 1, 2...
// Top 100 with steeper falloff
rng.zipf(100, 1.5); // 1, 1, 1, 2, 1, 1, 3...
// Flatter distribution
rng.zipf(10, 0.5); // 1, 3, 2, 5, 1, 4, 2...Use cases: Word frequencies, city populations, website traffic, popularity rankings.
chiSquared(k) → number (New in v1.1.0)
Chi-squared distribution with k degrees of freedom.
// 2 degrees of freedom
rng.chiSquared(2); // 0.47, 2.18, 1.23, 3.84...
// 5 degrees of freedom
rng.chiSquared(5); // 3.82, 5.47, 2.91, 6.28...
// 10 degrees of freedom
rng.chiSquared(10); // 8.12, 11.34, 9.47, 7.89...Use cases: Statistical testing, goodness-of-fit tests, variance analysis.
studentT(df) → number (New in v1.1.0)
Student's t-distribution for small sample statistics.
// 3 degrees of freedom (heavy tails)
rng.studentT(3); // -0.84, 1.47, -2.18, 0.23...
// 10 degrees of freedom (closer to normal)
rng.studentT(10); // 0.47, -1.12, 0.89, -0.34...
// 30 degrees of freedom (nearly normal)
rng.studentT(30); // 0.28, -0.67, 1.04, -0.18...Use cases: Confidence intervals, hypothesis testing, small sample analysis.
vonMises(mu, kappa) → number (New in v1.1.0)
Von Mises distribution for circular/directional data. Returns angle in radians [0, 2π].
// Concentrated around 0 (East)
rng.vonMises(0, 5); // 0.12, -0.08, 0.23, 6.18...
// Concentrated around π/2 (North)
rng.vonMises(Math.PI/2, 3); // 1.47, 1.62, 1.38, 1.71...
// Low concentration (nearly uniform)
rng.vonMises(0, 0.5); // 2.84, 0.47, 5.12, 3.91...Use cases: Wind direction, compass bearings, time of day (circular), animal migration angles.
hypergeometric(N, K, n) → number (New in v1.1.0)
Hypergeometric distribution - successes when sampling without replacement.
// Drawing 5 cards, 13 hearts in 52-card deck
rng.hypergeometric(52, 13, 5); // 1, 2, 0, 1, 2, 1...
// 10 defective items in 100, sample 20
rng.hypergeometric(100, 10, 20); // 2, 1, 3, 2, 2...
// Urn with 30 red balls of 50 total, draw 10
rng.hypergeometric(50, 30, 10); // 6, 5, 7, 6, 5...Use cases: Card games (probability of drawing specific cards), quality control sampling, lottery probabilities.
Array Utilities
shuffle(array) → array
Shuffles an array in place using Fisher-Yates algorithm. Returns the same array.
const deck = [1, 2, 3, 4, 5];
rng.shuffle(deck);
console.log(deck); // [3, 1, 5, 2, 4] (mutated)shuffled(array) → array
Returns a new shuffled array, leaving the original unchanged.
const original = [1, 2, 3, 4, 5];
const shuffled = rng.shuffled(original);
console.log(original); // [1, 2, 3, 4, 5] (unchanged)
console.log(shuffled); // [3, 1, 5, 2, 4] (new array)pick(array) → element
Returns a random element from the array.
const items = ['sword', 'shield', 'potion', 'scroll'];
rng.pick(items); // 'potion'
rng.pick(items); // 'sword'
rng.pick(items); // 'potion'sample(array, n) → array
Returns n random elements without replacement (no duplicates).
const pool = ['a', 'b', 'c', 'd', 'e', 'f'];
rng.sample(pool, 3); // ['c', 'a', 'e']
rng.sample(pool, 3); // ['b', 'f', 'd']weightedPick(items, weights) → element
Picks a random element with weighted probabilities.
const items = ['common', 'uncommon', 'rare', 'legendary'];
const weights = [70, 20, 8, 2]; // Must match items length
rng.weightedPick(items, weights); // 'common' (70% chance)
rng.weightedPick(items, weights); // 'common' (70% chance)
rng.weightedPick(items, weights); // 'rare' (8% chance)weightedPickObject(weightedItems) → string
Picks from an object where keys are items and values are weights.
const lootTable = {
gold: 50,
potion: 30,
weapon: 15,
artifact: 5
};
rng.weightedPickObject(lootTable); // 'gold'
rng.weightedPickObject(lootTable); // 'potion'
rng.weightedPickObject(lootTable); // 'gold'array(length, generator?) → array
Generates an array of random values using the provided generator function.
// Array of random floats
rng.array(5);
// [0.284, 0.917, 0.103, 0.558, 0.721]
// Array of random integers
rng.array(5, () => rng.int(1, 100));
// [42, 87, 13, 56, 91]
// Array of random booleans
rng.array(5, () => rng.bool(0.3));
// [false, true, false, false, false]
// Array of random points
rng.array(3, () => rng.pointInCircle(10));
// [{x: 3.2, y: -5.1}, {x: -2.8, y: 1.4}, {x: 7.1, y: 2.9}]Geometric Utilities
pointInCircle(radius?) → {x, y}
Returns a random point uniformly distributed inside a circle.
rng.pointInCircle(10);
// { x: 3.284, y: -5.917 }
rng.pointInCircle(1);
// { x: 0.284, y: 0.417 }pointOnCircle(radius?) → {x, y}
Returns a random point on the edge of a circle.
rng.pointOnCircle(10);
// { x: 7.071, y: 7.071 }
rng.pointOnCircle(1);
// { x: -0.866, y: 0.5 }pointInSphere(radius?) → {x, y, z}
Returns a random point uniformly distributed inside a sphere.
rng.pointInSphere(5);
// { x: 1.2, y: -2.8, z: 3.1 }pointOnSphere(radius?) → {x, y, z}
Returns a random point on the surface of a sphere.
rng.pointOnSphere(1);
// { x: 0.577, y: 0.577, z: 0.577 }direction2D() → {x, y}
Returns a random 2D unit vector (direction).
rng.direction2D();
// { x: 0.6, y: -0.8 } (length = 1)direction3D() → {x, y, z}
Returns a random 3D unit vector (direction).
rng.direction3D();
// { x: 0.33, y: 0.67, z: -0.67 } (length = 1)pointInRect(x, y, width, height) → {x, y}
Returns a random point inside a rectangle.
rng.pointInRect(0, 0, 100, 50);
// { x: 42.8, y: 31.2 }
rng.pointInRect(10, 20, 80, 60);
// { x: 54.2, y: 67.8 }pointInBox(x, y, z, width, height, depth) → {x, y, z}
Returns a random point inside a 3D box.
rng.pointInBox(0, 0, 0, 10, 10, 10);
// { x: 4.2, y: 7.1, z: 2.8 }Special Generators
uuid() → string
Generates a random UUID v4.
rng.uuid(); // 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
rng.uuid(); // '7c9e6679-7425-40de-944b-e07fc1f90ae7'color() → string
Generates a random hex color.
rng.color(); // '#7a3f9c'
rng.color(); // '#2ecc71'
rng.color(); // '#e74c3c'colorRGB() → {r, g, b}
Generates a random RGB color object.
rng.colorRGB();
// { r: 122, g: 63, b: 156 }colorHSL(saturation?, lightness?) → {h, s, l}
Generates a random HSL color. Optionally fix saturation and/or lightness.
// Fully random
rng.colorHSL();
// { h: 274, s: 42, l: 43 }
// Fixed saturation and lightness (random hue)
rng.colorHSL(80, 50);
// { h: 187, s: 80, l: 50 }
// Just fixed saturation
rng.colorHSL(100, null);
// { h: 42, s: 100, l: 67 }char(charset?) → string
Returns a random character from the given charset.
rng.char(); // 'k' (alphanumeric)
rng.char('0123456789'); // '7'
rng.char('aeiou'); // 'e'
rng.char('!@#$%'); // '#'string(length, charset?) → string
Generates a random string of the given length.
rng.string(8);
// 'Kj8mPx2n'
rng.string(16, '0123456789abcdef');
// 'a7f3c921e8b4d056'
rng.string(6, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
// 'KFMQZW'
rng.string(10, '01');
// '0110110101'State Management
State management allows you to save, restore, clone, and fork generators.
getState() → object
Returns the complete internal state for saving.
const state = rng.getState();
// Save to localStorage, file, database, etc.
localStorage.setItem('rngState', JSON.stringify(state));setState(state) → void
Restores from a previously saved state.
const savedState = JSON.parse(localStorage.getItem('rngState'));
rng.setState(savedState);
// Generator continues from exact saved positionreset() → void
Resets the generator to its initial seed.
const rng = new PRNG('my-seed');
rng.random(); // 0.284
rng.random(); // 0.917
rng.random(); // 0.103
rng.reset();
rng.random(); // 0.284 (back to start!)
rng.random(); // 0.917clone() → PRNG
Creates an independent copy at the current position.
const rng = new PRNG('seed');
rng.random(); // 0.284
rng.random(); // 0.917
const clone = rng.clone();
rng.random(); // 0.103
clone.random(); // 0.103 (same!)
rng.random(); // 0.558
clone.random(); // 0.558 (still in sync)fork(label?) → PRNG
Creates a new generator with a derived seed. Perfect for sub-systems.
const worldRng = new PRNG('world-42');
// Create independent streams for different systems
const terrainRng = worldRng.fork('terrain');
const riverRng = worldRng.fork('rivers');
const forestRng = worldRng.fork('forests');
const cityRng = worldRng.fork('cities');
// Each fork is independent but reproducible
terrainRng.random(); // Always same for 'world-42' + 'terrain'
riverRng.random(); // Different sequence
forestRng.random(); // Different sequence
// Recreating gives identical results
const worldRng2 = new PRNG('world-42');
const terrainRng2 = worldRng2.fork('terrain');
terrainRng2.random(); // Identical to terrainRng!Noise Generators
SeedForge includes 6 noise generators for procedural content generation.
Value Noise
const { Noise } = require('seedforge-prng');
const noise = new Noise.ValueNoise('seed');
// 1D noise (returns 0-1)
noise.noise1D(x);
noise.noise1D(1.5); // 0.628
// 2D noise (returns 0-1)
noise.noise2D(x, y);
noise.noise2D(1.5, 2.3); // 0.412
// 3D noise (returns 0-1)
noise.noise3D(x, y, z);
noise.noise3D(1.5, 2.3, 0.8); // 0.553Simplex Noise
const simplex = new Noise.SimplexNoise('seed');
// 2D noise (returns -1 to 1)
simplex.noise2D(x, y);
simplex.noise2D(1.5, 2.3); // 0.284
// 3D noise (returns -1 to 1)
simplex.noise3D(x, y, z);
simplex.noise3D(1.5, 2.3, 0.8); // -0.156Perlin Noise (New in v1.1.0)
Classic Perlin gradient noise.
const perlin = new Noise.PerlinNoise('seed');
// 2D noise (returns -1 to 1)
perlin.noise2D(x, y);
perlin.noise2D(1.5, 2.3); // 0.312
// 3D noise (returns -1 to 1)
perlin.noise3D(x, y, z);
perlin.noise3D(1.5, 2.3, 0.8); // -0.089Worley Noise (Cellular) (New in v1.1.0)
Cell/Voronoi distance-based noise for organic patterns.
const worley = new Noise.WorleyNoise('seed');
// Basic usage (returns distance to nearest cell point)
worley.noise2D(x, y);
// With distance type: 'euclidean', 'manhattan', 'chebyshev'
// F1 (nearest point)
worley.noise2D(1.5, 2.3, 'euclidean', 0); // Round cells
// F2 (second nearest)
worley.noise2D(1.5, 2.3, 'euclidean', 1); // Puffy shapes
// F2-F1 (cell edges)
worley.noise2D(1.5, 2.3, 'euclidean', 2); // Cracks/veins
// Manhattan distance - diamond cells
worley.noise2D(1.5, 2.3, 'manhattan', 0);
// Chebyshev distance - square cells
worley.noise2D(1.5, 2.3, 'chebyshev', 0);Use cases: Stone textures, biological cells, cracked earth, stained glass, caustics.
Ridged Noise (New in v1.1.0)
Sharp ridges and peaks, great for mountains and veins.
const ridged = new Noise.RidgedNoise('seed');
// 2D ridged noise
ridged.noise2D(x * 0.01, y * 0.01);
// With fBm for detailed mountain ridges
ridged.fbm(x * 0.01, y * 0.01, null, 6, 2.0, 0.5);Use cases: Mountain ridges, lightning bolts, veins, cracks, alien terrain.
Billowed Noise (New in v1.1.0)
Soft, puffy noise for clouds and soft terrain.
const billowed = new Noise.BillowedNoise('seed');
// 2D billowed noise
billowed.noise2D(x * 0.01, y * 0.01);
// With fBm for fluffy clouds
billowed.fbm(x * 0.01, y * 0.01, null, 4, 2.0, 0.5);Use cases: Clouds, smoke, soft hills, cotton, foam.
Fractal Brownian Motion (fBm)
All noise types support fBm for natural-looking, multi-scale noise:
// Parameters: x, y, z, octaves, lacunarity, persistence
noise.fbm(x, y, null, octaves, lacunarity, persistence);
// Defaults: 4 octaves, 2.0 lacunarity, 0.5 persistence
noise.fbm(1.5, 2.3);
// More detail (more octaves)
noise.fbm(1.5, 2.3, null, 8, 2.0, 0.5);
// Rougher terrain (higher persistence)
noise.fbm(1.5, 2.3, null, 6, 2.0, 0.65);
// 3D fBm
noise.fbm(1.5, 2.3, 0.8, 4, 2.0, 0.5);Parameters:
octaves- Number of noise layers (more = more detail)lacunarity- Frequency multiplier per octave (usually 2.0)persistence- Amplitude multiplier per octave (0.5 = each octave half as strong)
Turbulence (New in v1.1.0)
Chaotic, swirling noise using absolute values - great for fire, smoke, and plasma.
const simplex = new Noise.SimplexNoise('fire');
// Turbulence (absolute value fBm)
simplex.turbulence(x, y, null, octaves, lacunarity, persistence);
// Fire effect
simplex.turbulence(x * 0.03, y * 0.03, null, 6, 2.2, 0.5);
// Plasma
simplex.turbulence(x * 0.02, y * 0.02, null, 4, 2.5, 0.45);Use cases: Fire, smoke, plasma, marble veins, turbulent water.
Domain Warping (New in v1.1.0)
Distort coordinates using noise for organic, flowing patterns.
const simplex = new Noise.SimplexNoise('warp');
// Single warp - organic distortion
simplex.warp(x, y, warpStrength, octaves);
simplex.warp(x * 0.02, y * 0.02, 1.5, 4);
// Double warp - extreme organic patterns
simplex.warp2(x, y, warpStrength, octaves);
simplex.warp2(x * 0.02, y * 0.02, 2.0, 4);Use cases: Marble, alien terrain, organic materials, flowing patterns.
Usage Examples
Procedural Terrain Generation
const { PRNG, Noise } = require('seedforge-prng');
function generateHeightmap(seed, width, height) {
const noise = new Noise.SimplexNoise(seed);
const heightmap = [];
for (let y = 0; y < height; y++) {
const row = [];
for (let x = 0; x < width; x++) {
// Multi-octave noise for natural terrain
let elevation = noise.fbm(
x * 0.02, // Scale factor
y * 0.02,
null,
6, // 6 octaves for detail
2.0, // Standard lacunarity
0.5 // Standard persistence
);
// Normalize from [-1, 1] to [0, 1]
elevation = (elevation + 1) / 2;
row.push(elevation);
}
heightmap.push(row);
}
return heightmap;
}
// Same seed = same terrain every time
const terrain1 = generateHeightmap('world-42', 256, 256);
const terrain2 = generateHeightmap('world-42', 256, 256);
// terrain1 and terrain2 are identical!RPG Loot System
const { PRNG } = require('seedforge-prng');
class LootTable {
constructor(seed) {
this.rng = new PRNG(seed);
this.rarities = {
common: 60,
uncommon: 25,
rare: 10,
epic: 4,
legendary: 1
};
this.items = {
common: ['Rusty Sword', 'Wooden Shield', 'Cloth Armor', 'Minor Potion'],
uncommon: ['Iron Sword', 'Steel Shield', 'Leather Armor', 'Potion'],
rare: ['Silver Sword', 'Knight Shield', 'Chain Mail', 'Greater Potion'],
epic: ['Enchanted Blade', 'Dragon Shield', 'Plate Armor', 'Elixir'],
legendary: ['Excalibur', 'Aegis', 'Divine Armor', 'Phoenix Down']
};
}
generateLoot(enemyLevel) {
const loot = [];
// Number of drops (Poisson distribution)
const dropCount = this.rng.poisson(1 + enemyLevel * 0.3);
for (let i = 0; i < dropCount; i++) {
// Rarity (weighted, with level bonus)
const adjustedRarities = { ...this.rarities };
adjustedRarities.rare += enemyLevel;
adjustedRarities.epic += enemyLevel * 0.5;
adjustedRarities.legendary += enemyLevel * 0.2;
const rarity = this.rng.weightedPickObject(adjustedRarities);
const item = this.rng.pick(this.items[rarity]);
// Gold value (Pareto distribution for occasional high values)
const baseGold = { common: 10, uncommon: 50, rare: 200, epic: 1000, legendary: 5000 };
const gold = Math.round(this.rng.pareto(1.5, baseGold[rarity]));
loot.push({ item, rarity, value: gold });
}
return loot;
}
}
// Reproducible loot for specific encounters
const chest1 = new LootTable('dungeon-floor-3-chest-1');
console.log(chest1.generateLoot(10));
// Always gives the same loot for this chest!NPC Name Generator
const { PRNG } = require('seedforge-prng');
class NameGenerator {
constructor(seed) {
this.rng = new PRNG(seed);
this.prefixes = ['Ael', 'Bran', 'Cael', 'Dor', 'Eld', 'Fen', 'Gal', 'Hal', 'Ith', 'Jor'];
this.middles = ['', '', 'ar', 'en', 'il', 'or', 'un', 'al', 'em'];
this.suffixes = ['dric', 'wen', 'mir', 'thor', 'ius', 'ara', 'eth', 'ion', 'wyn', 'rick'];
this.titles = [
'the Brave', 'the Wise', 'the Swift', 'the Strong', 'the Cunning',
'Shadowbane', 'Ironforge', 'Lightbringer', 'Stormbringer', 'Flameheart'
];
this.professions = [
'Blacksmith', 'Merchant', 'Guard', 'Farmer', 'Innkeeper',
'Scholar', 'Hunter', 'Healer', 'Bard', 'Soldier'
];
}
generateName() {
return this.rng.pick(this.prefixes) +
this.rng.pick(this.middles) +
this.rng.pick(this.suffixes);
}
generateNPC(options = {}) {
const npc = {
name: this.generateName(),
profession: this.rng.pick(this.professions),
level: this.rng.int(1, 20),
stats: {
strength: Math.round(this.rng.normal(10, 3)),
intelligence: Math.round(this.rng.normal(10, 3)),
agility: Math.round(this.rng.normal(10, 3)),
charisma: Math.round(this.rng.normal(10, 3))
}
};
// 20% chance for a title
if (this.rng.bool(0.2)) {
npc.name += ' ' + this.rng.pick(this.titles);
}
return npc;
}
generateVillage(count) {
return this.rng.array(count, () => this.generateNPC());
}
}
// Generate consistent NPCs for a village
const village = new NameGenerator('starting-village');
console.log(village.generateVillage(5));
// Same village every time with seed 'starting-village'Dice Rolling System
const { PRNG } = require('seedforge-prng');
class DiceRoller {
constructor(seed) {
this.rng = new PRNG(seed);
}
// Roll XdY (e.g., 3d6 = roll 3 six-sided dice)
roll(count, sides) {
const rolls = [];
for (let i = 0; i < count; i++) {
rolls.push(this.rng.int(1, sides));
}
return {
rolls,
total: rolls.reduce((a, b) => a + b, 0),
min: Math.min(...rolls),
max: Math.max(...rolls)
};
}
// Roll with advantage (roll twice, take higher)
rollWithAdvantage(count, sides) {
const roll1 = this.roll(count, sides);
const roll2 = this.roll(count, sides);
return roll1.total >= roll2.total ? roll1 : roll2;
}
// Roll with disadvantage (roll twice, take lower)
rollWithDisadvantage(count, sides) {
const roll1 = this.roll(count, sides);
const roll2 = this.roll(count, sides);
return roll1.total <= roll2.total ? roll1 : roll2;
}
// Roll and drop lowest (e.g., 4d6 drop lowest for stats)
rollDropLowest(count, sides, drop = 1) {
const result = this.roll(count, sides);
const sorted = [...result.rolls].sort((a, b) => b - a);
const kept = sorted.slice(0, count - drop);
return {
rolls: result.rolls,
kept,
dropped: sorted.slice(count - drop),
total: kept.reduce((a, b) => a + b, 0)
};
}
}
// Seeded dice roller for reproducible combat
const combat = new DiceRoller('combat-encounter-42');
console.log('Attack roll:', combat.roll(1, 20));
// { rolls: [17], total: 17, min: 17, max: 17 }
console.log('Damage roll:', combat.roll(2, 6));
// { rolls: [4, 6], total: 10, min: 4, max: 6 }
console.log('Stat roll (4d6 drop lowest):', combat.rollDropLowest(4, 6));
// { rolls: [3, 5, 6, 2], kept: [6, 5, 3], dropped: [2], total: 14 }Card Deck Shuffling
const { PRNG } = require('seedforge-prng');
class Deck {
constructor(seed) {
this.rng = new PRNG(seed);
this.reset();
}
reset() {
this.cards = [];
const suits = ['♠', '♥', '♦', '♣'];
const ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
for (const suit of suits) {
for (const rank of ranks) {
this.cards.push({ rank, suit, name: `${rank}${suit}` });
}
}
}
shuffle() {
this.rng.shuffle(this.cards);
return this;
}
draw(count = 1) {
return this.cards.splice(0, count);
}
drawHand(size = 5) {
return this.draw(size);
}
remaining() {
return this.cards.length;
}
}
// Reproducible poker game
const deck = new Deck('poker-game-123');
deck.shuffle();
const player1 = deck.drawHand(5);
const player2 = deck.drawHand(5);
console.log('Player 1:', player1.map(c => c.name).join(' '));
// Player 1: 7♠ K♦ 3♥ J♣ 9♠
console.log('Player 2:', player2.map(c => c.name).join(' '));
// Player 2: A♥ 5♦ 2♣ Q♠ 8♦
// Same hands every time with seed 'poker-game-123'Procedural Colors
const { PRNG } = require('seedforge-prng');
class ColorPalette {
constructor(seed) {
this.rng = new PRNG(seed);
}
// Generate a cohesive color palette
generatePalette(count = 5) {
// Pick a random base hue
const baseHue = this.rng.int(0, 360);
const colors = [];
for (let i = 0; i < count; i++) {
// Vary hue within 60 degrees for cohesion
const hue = (baseHue + this.rng.int(-30, 30) + 360) % 360;
const saturation = this.rng.int(40, 80);
const lightness = this.rng.int(30, 70);
colors.push({
hsl: `hsl(${hue}, ${saturation}%, ${lightness}%)`,
hex: this.hslToHex(hue, saturation, lightness)
});
}
return colors;
}
// Generate complementary colors
generateComplementary() {
const hue1 = this.rng.int(0, 360);
const hue2 = (hue1 + 180) % 360;
const sat = this.rng.int(50, 80);
const light = this.rng.int(40, 60);
return [
{ hsl: `hsl(${hue1}, ${sat}%, ${light}%)` },
{ hsl: `hsl(${hue2}, ${sat}%, ${light}%)` }
];
}
hslToHex(h, s, l) {
s /= 100;
l /= 100;
const a = s * Math.min(l, 1 - l);
const f = n => {
const k = (n + h / 30) % 12;
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color).toString(16).padStart(2, '0');
};
return `#${f(0)}${f(8)}${f(4)}`;
}
}
const palette = new ColorPalette('my-game-theme');
console.log(palette.generatePalette(5));
// Consistent color palette for 'my-game-theme'Spawn Point Distribution
const { PRNG } = require('seedforge-prng');
class SpawnManager {
constructor(seed) {
this.rng = new PRNG(seed);
}
// Spawn enemies in a circle around a point
spawnInCircle(center, radius, count) {
const spawns = [];
for (let i = 0; i < count; i++) {
const point = this.rng.pointInCircle(radius);
spawns.push({
x: center.x + point.x,
y: center.y + point.y
});
}
return spawns;
}
// Spawn enemies on the edge of a circle (surrounding player)
spawnOnCircleEdge(center, radius, count) {
const spawns = [];
for (let i = 0; i < count; i++) {
const point = this.rng.pointOnCircle(radius);
spawns.push({
x: center.x + point.x,
y: center.y + point.y
});
}
return spawns;
}
// Spawn in a rectangle (room)
spawnInRoom(x, y, width, height, count) {
const spawns = [];
for (let i = 0; i < count; i++) {
const point = this.rng.pointInRect(x, y, width, height);
spawns.push(point);
}
return spawns;
}
// Spawn with minimum distance between points
spawnWithSpacing(area, count, minDistance) {
const spawns = [];
let attempts = 0;
const maxAttempts = count * 100;
while (spawns.length < count && attempts < maxAttempts) {
const point = this.rng.pointInRect(
area.x, area.y, area.width, area.height
);
// Check distance from all existing spawns
const valid = spawns.every(s => {
const dx = s.x - point.x;
const dy = s.y - point.y;
return Math.sqrt(dx * dx + dy * dy) >= minDistance;
});
if (valid) {
spawns.push(point);
}
attempts++;
}
return spawns;
}
}
const spawner = new SpawnManager('level-3-encounter-2');
const enemies = spawner.spawnInCircle({ x: 100, y: 100 }, 50, 8);
console.log(enemies);
// Same spawn positions every time!Save/Load Game State
const { PRNG } = require('seedforge-prng');
class Game {
constructor(seed) {
this.seed = seed;
this.rng = new PRNG(seed);
this.score = 0;
this.level = 1;
this.events = [];
}
play() {
// Simulate game events
const event = this.rng.weightedPickObject({
'found treasure': 30,
'encountered enemy': 40,
'discovered secret': 10,
'nothing happened': 20
});
this.events.push(event);
if (event === 'found treasure') {
this.score += this.rng.int(10, 100);
} else if (event === 'encountered enemy') {
// Combat roll
if (this.rng.int(1, 20) >= 10) {
this.score += this.rng.int(5, 25);
}
} else if (event === 'discovered secret') {
this.score += this.rng.int(50, 200);
this.level++;
}
return event;
}
// Save complete game state
save() {
return JSON.stringify({
seed: this.seed,
rngState: this.rng.getState(),
score: this.score,
level: this.level,
events: this.events
});
}
// Load game state
static load(saveData) {
const data = JSON.parse(saveData);
const game = new Game(data.seed);
game.rng.setState(data.rngState);
game.score = data.score;
game.level = data.level;
game.events = data.events;
return game;
}
}
// Play game
const game = new Game('player-save-1');
for (let i = 0; i < 10; i++) {
game.play();
}
console.log('Score:', game.score);
// Save
const saveData = game.save();
// Continue playing
for (let i = 0; i < 5; i++) {
game.play();
}
console.log('Score after more play:', game.score);
// Load and continue - exact same state!
const loadedGame = Game.load(saveData);
for (let i = 0; i < 5; i++) {
loadedGame.play();
}
console.log('Score from loaded game:', loadedGame.score);
// Both scores are identical!Parallel Random Streams
const { PRNG, Algorithms } = require('seedforge-prng');
// Method 1: Fork for sub-systems (recommended)
function createWorldGenerators(worldSeed) {
const world = new PRNG(worldSeed);
return {
terrain: world.fork('terrain'),
rivers: world.fork('rivers'),
forests: world.fork('forests'),
cities: world.fork('cities'),
npcs: world.fork('npcs'),
loot: world.fork('loot'),
weather: world.fork('weather')
};
}
const generators = createWorldGenerators('my-world');
// Each generator is independent
generators.terrain.random(); // Used for terrain
generators.rivers.random(); // Used for rivers
generators.cities.random(); // Used for cities
// Recreating gives identical results
const generators2 = createWorldGenerators('my-world');
// generators2.terrain produces same sequence as generators.terrain
// Method 2: Xoshiro jump() for massive parallelism
function createParallelStreams(seed, count) {
const streams = [];
for (let i = 0; i < count; i++) {
const stream = new Algorithms.Xoshiro128SS(seed);
// Each jump advances by 2^64 values - they'll never overlap
for (let j = 0; j < i; j++) {
stream.jump();
}
streams.push(stream);
}
return streams;
}
// Create 8 parallel streams for multi-threaded generation
const streams = createParallelStreams('base-seed', 8);
// Each stream can generate 2^64 values before overlapping with the nextMonte Carlo Simulation
const { PRNG } = require('seedforge-prng');
// Estimate Pi using Monte Carlo method
function estimatePi(seed, samples) {
const rng = new PRNG(seed);
let inside = 0;
for (let i = 0; i < samples; i++) {
const x = rng.random();
const y = rng.random();
if (x * x + y * y <= 1) {
inside++;
}
}
return 4 * inside / samples;
}
console.log('π estimate (10,000 samples):', estimatePi('pi-calc', 10000));
// π estimate (10,000 samples): 3.1412
console.log('π estimate (100,000 samples):', estimatePi('pi-calc', 100000));
// π estimate (100,000 samples): 3.14052
console.log('π estimate (1,000,000 samples):', estimatePi('pi-calc', 1000000));
// π estimate (1,000,000 samples): 3.141592
// Results are reproducible with the same seed!
// Simulate dice probabilities
function simulateDiceProbability(seed, diceCount, target, trials) {
const rng = new PRNG(seed);
let successes = 0;
for (let i = 0; i < trials; i++) {
let total = 0;
for (let d = 0; d < diceCount; d++) {
total += rng.int(1, 6);
}
if (total >= target) successes++;
}
return successes / trials;
}
console.log('P(2d6 >= 7):', simulateDiceProbability('dice', 2, 7, 100000));
// P(2d6 >= 7): 0.58342
// Expected: ~0.583 (58.3%)Performance
Benchmark generating 1,000,000 random numbers (approximate, varies by environment):
| Algorithm | Operations/sec | Notes |
|---|---|---|
sfc32 |
~45,000,000 | Fastest, excellent quality |
mulberry32 |
~42,000,000 | Very fast, good quality |
xoshiro128 |
~38,000,000 | Fast, excellent quality |
lcg |
~35,000,000 | Fast, lower quality |
pcg32 |
~25,000,000 | Good speed, best quality |
xorshift128 |
~15,000,000 | Slower in JS environments |
⚠️ Note:
xorshift128is slower due to its 128-bit state manipulation in JavaScript. Usesfc32orxoshiro128for performance-critical code.
Recommendations
- Games/Procedural Generation: Use
sfc32ormulberry32 - Simulations/Statistics: Use
pcg32 - General Purpose: Use
xoshiro128(default) - Legacy Compatibility: Use
lcg
Browser Usage
Script Tag
<!DOCTYPE html>
<html>
<head>
<script src="seedforge.js"></script>
</head>
<body>
<script>
// Access via PRNG global
const rng = new PRNG.PRNG('my-seed');
console.log(rng.random());
// Noise generators
const noise = new PRNG.Noise.SimplexNoise('terrain');
console.log(noise.noise2D(1.5, 2.3));
// Factory function
const rng2 = PRNG.create('another-seed', 'sfc32');
</script>
</body>
</html>ES Module (with bundler)
import { PRNG, Noise } from 'seedforge-prng';
const rng = new PRNG('seed');
const noise = new Noise.SimplexNoise('seed');TypeScript Support
SeedForge includes full TypeScript definitions in types/seedforge.d.ts.
import { PRNG, Noise, Point2D, RGB } from 'seedforge-prng';
const rng: PRNG = new PRNG('seed', 'pcg32');
const value: number = rng.random();
const integer: number = rng.int(1, 100);
const point: Point2D = rng.pointInCircle(10);
const color: RGB = rng.colorRGB();
const noise = new Noise.SimplexNoise('seed');
const height: number = noise.fbm(1.5, 2.3, null, 4);
const turb: number = noise.turbulence(1.5, 2.3, null, 6);Testing
Node.js
# Run test suite
npm test
# Run examples
npm run exampleBrowser
Open test/browser-test.html in your browser for an interactive test suite with:
- Unit Tests - Algorithm validation, distribution tests
- PRNG Demos - Random numbers, shuffle, colors, UUIDs, algorithm comparison
- Distribution Charts - Visual histograms with mean indicators
- Noise Gallery - All 6 noise types with click-to-enlarge (1024×1024)
- Procedural Textures - Terrain, stone, clouds, fire, marble, and more
License
MIT License - see LICENSE file.
Credits
Algorithm implementations based on:
- Mulberry32 by Tommy Ettinger
- Xoshiro128** by David Blackman and Sebastiano Vigna
- PCG by Melissa O'Neill (pcg-random.org)
- SFC32 by Chris Doty-Humphrey (PractRand)
- Simplex Noise based on Stefan Gustavson's implementation
- Perlin Noise by Ken Perlin
- Worley Noise by Steven Worley
Contributing
Contributions are welcome! Please feel free to submit issues and pull requests.
Changelog
1.1.0
New Statistical Distributions (6):
geometric(p)- Trials until first successzipf(n, s)- Power-law ranking distributionchiSquared(k)- Chi-squared distributionstudentT(df)- Student's t-distributionvonMises(mu, kappa)- Circular/angular distributionhypergeometric(N, K, n)- Sampling without replacement
New Noise Generators (4):
PerlinNoise- Classic Perlin gradient noiseWorleyNoise- Cellular/Voronoi noise with distance types (euclidean, manhattan, chebyshev)RidgedNoise- Sharp ridges for mountains, veinsBillowedNoise- Soft shapes for clouds, smoke
New Noise Features:
turbulence()- Chaotic absolute-value fBm for fire, smoke, plasmawarp()- Single domain warping for organic distortionwarp2()- Double domain warping for extreme organic patterns
Improvements:
- Enhanced browser test suite with interactive PRNG demos
- Click-to-enlarge noise previews (1024×1024)
- PRNG visualization section with canvas visualizations
- Professional distribution histograms with mean indicators
- 39 comprehensive unit tests
1.0.0
- Initial release
- 6 PRNG algorithms
- 11 statistical distributions
- Value Noise and Simplex Noise with fBm
- Full state management (save/load/clone/fork)
- TypeScript definitions
- Comprehensive test suite