Performant, modern noise generation for Haskell with a minimal dependency footprint.
- algebraic composition of noise functions. You can combine,
layer, and transform noise sources using standard operators (E.g.,
Num,Fractional,Monad, etc). - Complex effects like domain warping and multi-octave fractals with clean, type-safe composition.
- 84-95% of C++ FastNoiseLite performance through aggressive optimization and LLVM compilation.
For detailed FastNoiseLite comparison, methodology, and reproducibility instructions, see the benchmark README.
The public interface for this library is unlikely to change much, although the
implementations (noiseBaseN functions and anything in Numeric.Noise.Internal)
are subject to change and may change between minor versions.
- This project grew from a port of the excellent FastNoiseLite library. The library structure has been tuned to perform well in Haskell and fit well with Haskell semantics, but the core noise implementations are the same.
- All credit for the original design, algorithms, and implementation goes to its creator Jordan Peck (@Auburn). I'm grateful for their work and the opportunity to learn from it.
- The original FastNoiseLite code, from which the core algorithms in this library were originally ported, is (C) 2020 Jordan Peck and is licensed under the MIT license, a copy of which is included in this repository.
The library provides composable noise functions. Noise2 and Noise3 are type
aliases for 2D and 3D noise. Noise functions can be composed transparently using
standard operators with minimal performance cost.
Noise values are generally clamped to [-1, 1], although some noise functions
may occasionally produce values slightly outside this range.
import Numeric.Noise qualified as Noise
-- Compose multiple noise sources
myNoise2 :: (RealFrac a) => Noise.Seed -> a -> a -> a
myNoise2 =
let fractalConfig = Noise.defaultFractalConfig
combined = (Noise.perlin2 + Noise.superSimplex2) / 2
in Noise.noise2At $ Noise.fractal2 fractalConfig combinedThe library's unified Noise p v type enables powerful composition patterns:
The Monad instance is useful to create noise that depends on other noise values:
-- Use one noise function's output to modulate another
complexNoise :: Noise.Noise2 Float
complexNoise = do
baseNoise <- Noise.perlin2
detailNoise <- Noise.next2 Noise.superSimplex2
-- Blend based on base noise: smooth areas get less detail
pure $ baseNoise * 0.7 + detailNoise * (0.3 * (1 + baseNoise) / 2)This is especially useful for creating organic, varied terrain where one noise pattern influences the characteristics of another.
Generate 1D noise by slicing higher-dimensional noise at a fixed coordinate:
-- Create 1D noise by fixing one dimension
noise1d :: Noise.Noise1 Float
noise1d = Noise.sliceY2 0.0 Noise.perlin2
-- Evaluate at a point
value = Noise.noise1At noise1d seed 5.0Coordinate Transformation:
Scale, rotate, or warp the coordinate space:
-- Double the frequency
scaled = Noise.warp (\(x, y) -> (x * 2, y * 2)) Noise.perlin2
-- Rotate 45 degrees
rotated = Noise.warp (\(x, y) ->
let a = pi / 4
in (x * cos a - y * sin a, x * sin a + y * cos a)) Noise.perlin2Use reseed or next2/next3 to create independent layers:
layered = (Noise.perlin2 + Noise.next2 Noise.perlin2) / 2More examples can be found in bench and demo.
Domain warping uses one noise function to distort the coordinate space of another, creating organic, flowing patterns ideal for terrain, clouds, and natural textures:
domainWarped :: Noise.Noise2 Float
domainWarped = do
-- Generate 3D fractal for warp offsets
let warpNoise = Noise.fractal3 Noise.defaultFractalConfig{Noise.octaves = 5} Noise.perlin3
-- Sample 3D noise at different slices to create warp offsets
warpX <- Noise.sliceX3 0.0 warpNoise -- Samples at (0, x, y)
warpY <- Noise.sliceY3 0.0 warpNoise -- Samples at (x, 0, y)
-- Apply warping to base noise coordinates
Noise.warp (\(x, y) -> (x + 30 * warpX, y + 30 * warpY))
$ Noise.fractal2 Noise.defaultFractalConfig{Noise.octaves = 5} Noise.openSimplex2See the demo app for an interactive version with adjustable parameters.
- In single-threaded scenarios with LLVM enabled, this library achieves 84-95% of C++ FastNoiseLite performance.
- This library benefits considerably from compilation with the LLVM backend
(
-fllvm). Benchmarks suggest a ~50-80% difference depending on the kind of noise.
This library integrates well with massiv for parallel computation. Parallel performance can reach 10-15x single-threaded performance.
This is the recommended approach for generating large noise textures or datasets.
Measured by values / second generated by the noise functions. These results come
from a benchmark with -fllvm enabled.
There's inevitably some noise in the measurements because all of the results are forced into an unboxed vector.
| name | Float (values/sec) | Double (values/sec) |
|---|---|---|
| value2 | 173_511_654 | 189_119_731 |
| perlin2 | 154_674_464 | 161_114_532 |
| openSimplex2 | 74_747_031 | 74_332_345 |
| valueCubic2 | 61_415_544 | 62_481_313 |
| superSimplex2 | 51_295_369 | 50_383_577 |
| cellular2 | 34_996_382 | 32_652_899 |
| name | Float (values/sec) | Double (values/sec) |
|---|---|---|
| value3 | 90_805_572 | 93_188_363 |
| perlin3 | 74_080_032 | 82_477_882 |
| valueCubic3 | 18_765_912 | 18_284_749 |
There's an interactive demo app in the demo directory.





