Fast, zero-allocation procedural noise library for Crystal implementing the Sol-Vin Two-Tier Engine & Declarative Builder (core/ + dsl/) architecture.
-
Tier 1 Zero-Allocation Core Engines (
PerlinNoise::Core):-
Improved Gradient Perlin Noise (
PerlinEngine): 1D, 2D, 3D, and 4D sampling, periodic/tileable noise (sample_periodic_2d/3d), and exact analytic partial derivatives (sample_with_derivative_2d/3d). -
Simplex Noise (
SimplexEngine&SimplexNoise): 1D, 2D, 3D, and 4D isotropic simplex noise. -
Smoothed Value Noise (
ValueEngine): 1D, 2D, and 3D integer-lattice smoothed value noise. -
Worley / Cellular Noise (
WorleyEngine): 2D and 3D Voronoi/cellular noise withEuclidean,EuclideanSquared,Manhattan, andChebyshevmetrics andF1,F2,F2MinusF1,F1PlusF2,F1TimesF2, andCellValuefeature outputs. -
Multi-Octave Fractals (
FractalSampler):FBM(Fractional Brownian Motion),Billow,Ridged(Ridged Multifractal), andTurbulencewith configurableoctaves,persistence, andlacunarity. -
Domain Warping (
DomainWarp): Single- and multi-stage iterative coordinate warping in 2D and 3D. -
Batch Heightmaps & Volumes (
NoiseMap2D&NoiseMap3D): Planar maps, 100% seamless 4D-torus tileable textures, equirectangular spherical planetary maps, Sobel normal maps (#to_normal_map), terracing (#terrace!), and ASCII preview rendering (#to_ascii).
-
Improved Gradient Perlin Noise (
-
Tier 2 Declarative DSL (
PerlinNoise::DSL):-
PerlinNoise.build/SimplexNoise.buildblock configuration DSL. -
PerlinNoise.pipelinemulti-layer procedural graph withblend,select_layer,terrace,scale_bias,curve,clamp,abs, andinvert. -
PerlinNoise.biome_mapdeclarative 2D elevation$\times$ moisture biome classifier.
-
Add this to your application's shard.yml:
dependencies:
perlin_noise:
github: sol-vin/perlin_noiseThen run:
shards installrequire "perlin_noise"
p = PerlinNoise.new(1234) # Seed the generator
# Configure step (scale), persistence, octaves, and coordinate offsets
p.step = 0.4 # Default is 0.123456 (aliases: scale, frequency)
p.persistence = 0.4 # Default is 0.2 (legacy alias `persistance` also supported)
p.octaves = 4.0 # Default is 1.0 (alias: octave)
# Continuous 1D, 2D, 3D, and 4D noise in [-1.0, 1.0] (accepts Int or Float)
p.noise(0) # 1D
p.noise(1.5, 2.75) # 2D
p.noise(0, 1, 2) # 3D
p.noise_4d(0, 1, 2, 3) # 4D
# Normalized noise in [0.0, 1.0]
p.normalize_noise(10, 20)
# Heightmap helper
p.height(0, 0, 10) # 2D integer height in 0..10
p.height(0, 0, 5..25) # 2D integer height in 5..25
# Deterministic integers & PRNG integers
p.int(0, 10, 20) # 1D in [10, 20)
p.int(0, 1, 10, 20) # 2D in [10, 20)
p.int(0, 1, 2, 10..20) # 3D in 10..20
p.prng_int(0, 1, 2, 1, 50) # 3D PRNG-seeded integer
# Deterministic floats
p.float(0, 0.0, 1.0)
p.float(0, 1, 0.0, 5.0)
p.float(0, 1, 2, 1.0..10.0)
# Deterministic booleans
p.bool(0, 1, 2, 1, 100) # 1 out of 100 chance of being true
p.bool(10, 20, probability: 0.25) # 25% probability of being true
# Deterministic collection & weighted item selection
items = [1, 2, 3, "Bacon", "Taco", "Cat"]
p.item(0, 1, 2, items)
p.prng_item(0, 1, 2, items)
loot = {:common => 70, :rare => 25, :legendary => 5}
p.weighted_item(12, 34, loot) # => :common | :rare | :legendaryterrain = PerlinNoise.build do
seed 1337
algorithm :simplex # :perlin | :simplex | :value | :worley
fractal :ridged # :none | :fbm | :billow | :ridged | :turbulence
octaves 6
persistence 0.5
lacunarity 2.0
scale 0.015
offset x: 100.0, y: -50.0, z: 0.0
interpolation :quintic # :linear | :cosine | :hermite | :quintic
domain_warp amplitude: 8.0, frequency: 0.04, octaves: 2, iterations: 2
output_range 0.0..255.0
end
elevation = terrain.noise(42.5, 19.2)Combine multiple noise generators and post-processing modifiers into a single composable pipeline:
world = PerlinNoise.pipeline(seed: 2026) do
layer :continents do
algorithm :simplex
fractal :fbm
octaves 4
scale 0.008
end
layer :mountains do
algorithm :perlin
fractal :ridged
octaves 6
scale 0.03
end
layer : tectonic_mask do
algorithm :worley
worley metric: :euclidean, return_type: :f2_minus_f1, jitter: 0.85
scale 0.015
end
select_layer control: :tectonic_mask, low: :continents, high: :mountains, threshold: 0.1, falloff: 0.15
terrace 8, smoothness: 0.25
clamp -1.0..1.0
end
height_val = world.noise(120.0, 85.0)
map = world.map_2d(64, 32)
puts map.to_asciibiomes = PerlinNoise.biome_map(seed: 42) do
elevation do
algorithm :simplex
fractal :fbm
octaves 5
scale 0.01
end
moisture do
algorithm :perlin
fractal :fbm
octaves 3
scale 0.02
end
rule :deep_ocean, elevation: -1.0..-0.35
rule :ocean, elevation: -0.35..0.0
rule :beach, elevation: 0.0..0.08
rule :snow_peak, elevation: 0.65..1.0, moisture: 0.0..1.0
rule :tundra, elevation: 0.65..1.0, moisture: -1.0..0.0
rule :rainforest, elevation: 0.08..0.65, moisture: 0.3..1.0
rule :desert, elevation: 0.08..0.65, moisture: -1.0..-0.3
fallback :grassland
end
biomes.classify(100, 200) # => :rainforestgen = PerlinNoise.build(seed: 77) do
algorithm :perlin
fractal :fbm
octaves 4
scale 0.05
end
# 1. Planar 2D heightmap
planar = gen.map_2d(128, 128)
# 2. 100% seamless X/Y tileable texture (via 4D torus projection)
tileable = gen.tileable_map_2d(128, 128, frequency: 4.0)
# 3. Seamless equirectangular spherical planetary map
planet = gen.sphere_map_2d(256, 128)
# Export grayscale bytes, RGB tangent-space normal maps, or ASCII preview
bytes = tileable.to_bytes
normals = tileable.to_normal_map(bump_height: 2.5)
puts gen.map_2d(40, 12).to_asciigen = PerlinNoise.new(42)
# Exact 2D/3D value + partial derivatives {value, dx, dy[, dz]}
d2 = gen.derivative(12.3, 45.6)
d3 = gen.derivative(12.3, 45.6, 7.8)
# Periodic Perlin noise repeating every (px, py) lattice cells
v0 = gen.periodic_noise(1.5, 2.5, 16, 16)
v1 = gen.periodic_noise(17.5, 18.5, 16, 16) # == v0- Fork it (https://github.com/sol-vin/perlin_noise/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Run tests and formatter (
crystal spec && crystal tool format --check) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request