A TypeScript monorepo for procedural generation primitives — initially powering a space game but designed as a general-purpose engine for generative art, tile maps, and visual tooling.
| Package | Description |
|---|---|
@anomnomnomaly/prng |
Seeded PRNG engines: xoshiro256**, mulberry32, squirrel3 |
@anomnomnomaly/noise |
Coherent noise: simplex2D, worley2D, fBm combinator |
@anomnomnomaly/sample |
Spatial distributions: Poisson disk, Delaunay, Voronoi, Halton |
@anomnomnomaly/filters |
Post-processing: color interpolation, remap, smoothstep, dithering |
Injectable RNG. Every function that needs randomness accepts a () => number rather than maintaining internal state or calling Math.random directly. Seed once, pass everywhere — reproducibility is free.
const rng = xoshiro256({ seed: 12345 });
const points = poissonDisk({ width: 800, height: 600, minDistance: 30 }, rng);Functional, data-last. Configuration objects come first, the RNG or data comes last, keeping partial application and composition clean.
Shared type vocabulary. The core types live in @anomnomnomaly/prng and flow through the whole graph:
type RandomFn = () => number;
type HashFn = (position: number, seed?: number) => number;
type NoiseFn2D = (x: number, y: number) => number;Because simplex2D, worley2DAsNoise, and fbm all satisfy NoiseFn2D, they compose freely without any adapter code:
const terrain = fbm(simplex2D(rng), { octaves: 6, persistence: 0.5 });
const cellular = fbm(worley2DAsNoise(rng), { octaves: 4 });
const v = terrain(x, y); // both call sites identical@anomnomnomaly/prng
↓
@anomnomnomaly/noise
@anomnomnomaly/sample
@anomnomnomaly/filters
noise, sample, and filters depend on prng for the RandomFn interface type. They don't depend on each other, keeping the graph flat and letting consumers install only what they need.
npm install
npm run build # builds prng first, then noise/sample/filters in parallel
npm run typecheck # tsc --noEmit across all packages
npm test # vitest run across all packages
npm run clean # removes all dist/ output