Procedural Generation (Procgen)
@esengine/procgen provides core tools for procedural content generation, including noise functions, seeded random numbers, and various random utilities.
Installation
Section titled “Installation”npm install @esengine/procgenQuick Start
Section titled “Quick Start”Noise Generation
Section titled “Noise Generation”import { createPerlinNoise, createFBM } from '@esengine/procgen';
// Create Perlin noiseconst perlin = createPerlinNoise(12345); // seed
// Sample 2D noiseconst value = perlin.noise2D(x * 0.1, y * 0.1);console.log(value); // [-1, 1]
// Use FBM for more natural resultsconst fbm = createFBM(perlin, { octaves: 6, persistence: 0.5});
const height = fbm.noise2D(x * 0.01, y * 0.01);Seeded Random
Section titled “Seeded Random”import { createSeededRandom } from '@esengine/procgen';
// Create deterministic random generatorconst rng = createSeededRandom(42);
// Same seed always produces same sequenceconsole.log(rng.next()); // 0.xxxconsole.log(rng.nextInt(1, 100)); // 1-100console.log(rng.nextBool(0.3)); // 30% trueWeighted Random
Section titled “Weighted Random”import { createWeightedRandom, createSeededRandom } from '@esengine/procgen';
const rng = createSeededRandom(42);
const loot = createWeightedRandom([ { value: 'common', weight: 60 }, { value: 'uncommon', weight: 25 }, { value: 'rare', weight: 10 }, { value: 'legendary', weight: 5 }]);
const drop = loot.pick(rng);console.log(drop); // Likely 'common'Noise Functions
Section titled “Noise Functions”Perlin Noise
Section titled “Perlin Noise”Classic gradient noise, output range [-1, 1]:
import { createPerlinNoise } from '@esengine/procgen';
const perlin = createPerlinNoise(seed);const value2D = perlin.noise2D(x, y);const value3D = perlin.noise3D(x, y, z);Simplex Noise
Section titled “Simplex Noise”Faster than Perlin, less directional bias:
import { createSimplexNoise } from '@esengine/procgen';
const simplex = createSimplexNoise(seed);const value = simplex.noise2D(x, y);Worley Noise
Section titled “Worley Noise”Cell-based noise for stone, cell textures:
import { createWorleyNoise } from '@esengine/procgen';
const worley = createWorleyNoise(seed);const distance = worley.noise2D(x, y);FBM (Fractal Brownian Motion)
Section titled “FBM (Fractal Brownian Motion)”Layer multiple noise octaves for richer detail:
import { createPerlinNoise, createFBM } from '@esengine/procgen';
const baseNoise = createPerlinNoise(seed);
const fbm = createFBM(baseNoise, { octaves: 6, // Layer count (more = richer detail) lacunarity: 2.0, // Frequency multiplier persistence: 0.5, // Amplitude decay frequency: 1.0, // Initial frequency amplitude: 1.0 // Initial amplitude});
// Standard FBMconst value = fbm.noise2D(x, y);
// Ridged FBM (for mountains)const ridged = fbm.ridged2D(x, y);
// Turbulenceconst turb = fbm.turbulence2D(x, y);
// Billowed (for clouds)const cloud = fbm.billowed2D(x, y);Seeded Random API
Section titled “Seeded Random API”SeededRandom
Section titled “SeededRandom”Deterministic PRNG based on xorshift128+:
import { createSeededRandom } from '@esengine/procgen';
const rng = createSeededRandom(42);Basic Methods
Section titled “Basic Methods”rng.next(); // [0, 1) floatrng.nextInt(1, 10); // [min, max] integerrng.nextFloat(0, 100); // [min, max) floatrng.nextBool(); // 50%rng.nextBool(0.3); // 30%rng.reset(); // Reset to initial stateDistribution Methods
Section titled “Distribution Methods”// Normal distribution (Gaussian)rng.nextGaussian(); // mean 0, stdDev 1rng.nextGaussian(100, 15); // mean 100, stdDev 15
// Exponential distributionrng.nextExponential(); // λ = 1rng.nextExponential(0.5); // λ = 0.5Geometry Methods
Section titled “Geometry Methods”// Uniform point in circleconst point = rng.nextPointInCircle(50); // { x, y }
// Point on circle edgeconst edge = rng.nextPointOnCircle(50); // { x, y }
// Uniform point in sphereconst point3D = rng.nextPointInSphere(50); // { x, y, z }
// Random direction vectorconst dir = rng.nextDirection2D(); // { x, y }, length 1Weighted Random API
Section titled “Weighted Random API”WeightedRandom
Section titled “WeightedRandom”Precomputed cumulative weights for efficient selection:
import { createWeightedRandom } from '@esengine/procgen';
const selector = createWeightedRandom([ { value: 'apple', weight: 5 }, { value: 'banana', weight: 3 }, { value: 'cherry', weight: 2 }]);
const result = selector.pick(rng);const result2 = selector.pickRandom(); // Uses Math.random
console.log(selector.getProbability(0)); // 0.5 (5/10)console.log(selector.size); // 3console.log(selector.totalWeight); // 10Convenience Functions
Section titled “Convenience Functions”import { weightedPick, weightedPickFromMap } from '@esengine/procgen';
const item = weightedPick([ { value: 'a', weight: 1 }, { value: 'b', weight: 2 }], rng);
const item2 = weightedPickFromMap({ 'common': 60, 'rare': 30, 'epic': 10}, rng);Shuffle and Sampling
Section titled “Shuffle and Sampling”shuffle / shuffleCopy
Section titled “shuffle / shuffleCopy”Fisher-Yates shuffle:
import { shuffle, shuffleCopy } from '@esengine/procgen';
const arr = [1, 2, 3, 4, 5];shuffle(arr, rng); // In-placeconst shuffled = shuffleCopy(arr, rng); // CopypickOne
Section titled “pickOne”import { pickOne } from '@esengine/procgen';
const item = pickOne(['a', 'b', 'c', 'd'], rng);sample / sampleWithReplacement
Section titled “sample / sampleWithReplacement”import { sample, sampleWithReplacement } from '@esengine/procgen';
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];const unique = sample(arr, 3, rng); // 3 uniqueconst withRep = sampleWithReplacement(arr, 5, rng); // 5 with replacementrandomIntegers
Section titled “randomIntegers”import { randomIntegers } from '@esengine/procgen';
// 5 unique random integers from 1-100const nums = randomIntegers(1, 100, 5, rng);weightedSample
Section titled “weightedSample”import { weightedSample } from '@esengine/procgen';
const items = ['A', 'B', 'C', 'D', 'E'];const weights = [10, 8, 6, 4, 2];const selected = weightedSample(items, weights, 3, rng);Practical Examples
Section titled “Practical Examples”Procedural Terrain
Section titled “Procedural Terrain”import { createPerlinNoise, createFBM } from '@esengine/procgen';
class TerrainGenerator { private fbm: FBM; private moistureFbm: FBM;
constructor(seed: number) { const heightNoise = createPerlinNoise(seed); const moistureNoise = createPerlinNoise(seed + 1000);
this.fbm = createFBM(heightNoise, { octaves: 8, persistence: 0.5, frequency: 0.01 });
this.moistureFbm = createFBM(moistureNoise, { octaves: 4, persistence: 0.6, frequency: 0.02 }); }
getHeight(x: number, y: number): number { let height = this.fbm.noise2D(x, y); height += this.fbm.ridged2D(x * 0.5, y * 0.5) * 0.3; return (height + 1) * 0.5; // Normalize to [0, 1] }
getBiome(x: number, y: number): string { const height = this.getHeight(x, y); const moisture = (this.moistureFbm.noise2D(x, y) + 1) * 0.5;
if (height < 0.3) return 'water'; if (height < 0.4) return 'beach'; if (height > 0.8) return 'mountain';
if (moisture < 0.3) return 'desert'; if (moisture > 0.7) return 'forest'; return 'grassland'; }}Loot System
Section titled “Loot System”import { createSeededRandom, createWeightedRandom } from '@esengine/procgen';
class LootSystem { private rng: SeededRandom; private raritySelector: WeightedRandom<string>;
constructor(seed: number) { this.rng = createSeededRandom(seed); this.raritySelector = createWeightedRandom([ { value: 'common', weight: 60 }, { value: 'uncommon', weight: 25 }, { value: 'rare', weight: 10 }, { value: 'legendary', weight: 5 } ]); }
generateLoot(count: number): LootItem[] { const loot: LootItem[] = []; for (let i = 0; i < count; i++) { const rarity = this.raritySelector.pick(this.rng); // Get item from rarity table... loot.push(item); } return loot; }}Blueprint Nodes
Section titled “Blueprint Nodes”Noise Nodes
Section titled “Noise Nodes”SampleNoise2D- Sample 2D noiseSampleFBM- Sample FBM noise
Random Nodes
Section titled “Random Nodes”SeededRandom- Generate random floatSeededRandomInt- Generate random integerWeightedPick- Weighted random selectionShuffleArray- Shuffle arrayPickRandom- Pick random elementSampleArray- Sample from arrayRandomPointInCircle- Random point in circle
Best Practices
Section titled “Best Practices”-
Use seeds for reproducibility
const seed = Date.now();const rng = createSeededRandom(seed);saveSeed(seed); -
Precompute weighted selectors
// Good: Create once, use many timesconst selector = createWeightedRandom(items);for (let i = 0; i < 1000; i++) {selector.pick(rng);} -
Choose appropriate noise
- Perlin: Smooth terrain, clouds
- Simplex: Performance-critical
- Worley: Cell textures, stone
- FBM: Natural multi-detail effects
-
Tune FBM parameters
octaves: More = richer detail, higher costpersistence: 0.5 is common, higher = more high-frequency detaillacunarity: Usually 2, controls frequency growth
Documentation
Section titled “Documentation”- Noise Functions - Perlin, Simplex, Worley, FBM
- Seeded Random - SeededRandom API and distribution methods
- Sampling Utilities - Weighted random, shuffle, sampling
- Examples - Terrain, loot, level generation