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”npminstall@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 multiplierpersistence: 0.5, // Amplitude decayfrequency: 1.0, // Initial frequencyamplitude: 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.randomconsole.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';classTerrainGenerator {private fbm:FBM;private moistureFbm:FBM;constructor(seed:number) {constheightNoise = createPerlinNoise(seed);constmoistureNoise = 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 {letheight = 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 {constheight = this.getHeight(x, y);constmoisture = (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';classLootSystem {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[] {constloot:LootItem[] = [];for (leti = 0; i < count; i++) {constrarity = 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