pure-noise: High-performance composable noise generation (Perlin, Simplex, Cellular)

[ bsd3, library, math, noise, numeric ] [ Propose Tags ] [ Report a vulnerability ]

A high-performance noise generation library ported from FastNoiseLite. Provides N-dimensional noise functions (Perlin, OpenSimplex, SuperSimplex, Value, Cellular) that can be composed using Num or Fractional methods with minimal performance overhead. Noise values are generally clamped to [-1, 1]. Benefits significantly from LLVM backend compilation (~50-80% performance improvement).


[Skip to Readme]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0.0, 0.1.0.1, 0.2.0.0, 0.2.1.0, 0.2.1.1
Change log CHANGELOG.md
Dependencies base (>=4.16 && <5), primitive (>=0.8 && <0.10) [details]
Tested with ghc ==9.6.7, ghc ==9.8.4, ghc ==9.10.2
License BSD-3-Clause
Copyright 2024 Jeremy Nuttall
Author Jeremy Nuttall
Maintainer jeremy@jeremy-nuttall.com
Category Math, Numeric, Noise
Home page https://github.com/jtnuttall/pure-noise#readme
Bug tracker https://github.com/jtnuttall/pure-noise/issues
Source repo head: git clone https://github.com/jtnuttall/pure-noise
Uploaded by jtnuttall at 2025-10-31T14:12:34Z
Distributions NixOS:0.1.0.1
Downloads 83 total (15 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs uploaded by user
Build status unknown [no reports yet]

Readme for pure-noise-0.2.1.1

[back to package description]

pure-noise

Performant, modern noise generation for Haskell with a minimal dependency footprint.

Core features

  • 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.

Acknowledgments

  • 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.

Usage

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.

Basic Example

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 combined

Advanced Features

The library's unified Noise p v type enables powerful composition patterns:

Complex Compositions

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.

1D Noise via Slicing

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.0

Coordinate 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.perlin2

Layering Independent Noise

Use reseed or next2/next3 to create independent layers:

layered = (Noise.perlin2 + Noise.next2 Noise.perlin2) / 2

More examples can be found in bench and demo.

Domain Warping

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.openSimplex2

Domain Warped Noise

See the demo app for an interactive version with adjustable parameters.

Performance notes

  • 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.

Parallel noise generation

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.

Benchmarks

Results

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.

2D
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
3D
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

Examples

There's an interactive demo app in the demo directory.

OpenSimplex2

OpenSimplex2 OpenSimplex2 ridged

Perlin

Perlin fBm

Cellular

value distance2add