Skip to content

Getting started using FastNoise2

Jordan Peck edited this page Feb 10, 2026 · 6 revisions

Getting Started with FastNoise2

This guide covers the fundamentals of using FastNoise2 in your C++ code. Make sure you have already included the library in your project setup.

Include the Header

All you need is a single include:

#include <FastNoise/FastNoise.h>

This gives you access to all node types, SmartNode, and the generation functions.

Creating Nodes

Nodes are created with FastNoise::New<T>() which returns a SmartNode<T> - a reference-counted smart pointer that manages the node's lifetime automatically.

auto simplex = FastNoise::New<FastNoise::Simplex>();

Nodes cannot be copied or moved directly. Always work with them through SmartNode.

Building a Node Tree

FastNoise2 uses a node graph architecture where you connect nodes together to build complex noise. A simple example is wrapping a noise generator in a fractal:

// Create the base noise generator
auto simplex = FastNoise::New<FastNoise::Simplex>();

// Create a fractal and connect the simplex as its source
auto fractal = FastNoise::New<FastNoise::FractalFBm>();
fractal->SetSource( simplex );
fractal->SetOctaveCount( 5 );

You can build arbitrarily complex trees by chaining nodes:

// Simplex noise -> FBm fractal -> domain warp
auto simplex = FastNoise::New<FastNoise::Simplex>();

auto fractal = FastNoise::New<FastNoise::FractalFBm>();
fractal->SetSource( simplex );
fractal->SetOctaveCount( 4 );

auto warp = FastNoise::New<FastNoise::DomainWarpGradient>();
warp->SetSource( fractal );
warp->SetWarpAmplitude( 50.0f );

Make sure all sources are set for a node before generating any noise, failure to do so will cause a nullptr access crash. This is only an issue when creating node trees in code, the Node Editor cannot export an invalid node tree

Always call the generation function on the root (outermost) node of your tree. In the example above, that's warp, not simplex.

Generating Noise

2D Uniform Grid (textures, heightmaps)

Ideal for generating noise for 2D textures, heightmaps, or image buffers that require uniform spacing between sample positions:

const int width = 256;
const int height = 256;
std::vector<float> noiseData( width * height );

auto minMax = generator->GenUniformGrid2D(
    noiseData.data(),
    0.0f, 0.0f,       // xOffset, yOffset - starting position in world space
    width, height,     // xCount, yCount - number of samples per axis
    1.0f, 1.0f,       // xStepSize, yStepSize - distance between samples
    1337               // seed
);

// minMax.min and minMax.max tell you the range of generated values
// Output is in row-major order: noiseData[y * width + x]

Step size controls how zoomed in the noise is. Smaller values (e.g. 0.5) zoom in, larger values (e.g. 2.0) zoom out. This is independent of the node's Feature Scale setting.

3D Uniform Grid (voxel terrain, volumes)

For volumetric data like voxel terrain or 3D textures:

const int sizeX = 64, sizeY = 64, sizeZ = 64;
std::vector<float> noiseData( sizeX * sizeY * sizeZ );

generator->GenUniformGrid3D(
    noiseData.data(),
    0.0f, 0.0f, 0.0f,       // starting position in world space
    sizeX, sizeY, sizeZ,     // sample counts
    1.0f, 1.0f, 1.0f,        // step sizes
    1337                      // seed
);

// Output order: noiseData[(z * sizeY + y) * sizeX + x]

Performance tip: Avoid setting xCount = 1 when you want a 2D slice of 3D noise. Due to how positions are generated internally, a small xCount is bad for performance. Use yCount = 1 or zCount = 1 instead to get a slice.

Tileable 2D Noise (seamless textures)

For textures that need to tile seamlessly:

const int tileSize = 256;
std::vector<float> noiseData( tileSize * tileSize );

generator->GenTileable2D(
    noiseData.data(),
    tileSize, tileSize,  // tile dimensions
    1.0f, 1.0f,          // step sizes
    1337                  // seed
);

Note: Tileable generation works by mapping onto 4D coordinates internally, so the underlying noise types will be using 4D generation.

Position Arrays (fastest for repeated generation)

Can be used for sampling at non-uniform positions (mesh vertices, particle positions, etc.):

GenPositionArray is actually faster than GenUniformGrid because the uniform grid positions don't need to be generated each call. For maximum generation performance across many calls, pre-generate your own position arrays once and reuse them, using the offset parameters to shift the sampling region:

// Pre-generate positions once
std::vector<float> xPositions( 256 * 256 );
std::vector<float> yPositions( 256 * 256 );
for( int y = 0; y < 256; y++ )
    for( int x = 0; x < 256; x++ )
    {
        xPositions[y * 256 + x] = (float)x;
        yPositions[y * 256 + x] = (float)y;
    }

// Reuse positions for each generation call, shifting with offsets
std::vector<float> noiseData( 256 * 256 );
generator->GenPositionArray2D(
    noiseData.data(),
    (int)xPositions.size(),
    xPositions.data(), yPositions.data(),
    chunkX, chunkY,  // offsets shift positions without modifying the arrays
    1337              // seed
);

Single Value Lookups (VERY SLOW)

GenSingle is significantly slower per-sample than the batch methods because SIMD lanes are underutilised. Avoid using it unless you truly only need a single sample:

float value = generator->GenSingle2D( 10.5f, 20.3f, 1337 );

Do not use GenSingle in a loop. If you need multiple values, use GenPositionArray or GenUniformGrid instead.

Loading from the Node Editor

The Node Editor lets you visually create node trees and export them as encoded strings. To load one:

auto generator = FastNoise::NewFromEncodedNodeTree( "DQkGDA==" );

if( generator )
{
    std::vector<float> noise( 256 * 256 );
    generator->GenUniformGrid2D( noise.data(), 0, 0, 256, 256, 1, 1, 1337 );
}

To get an encoded string from the Node Editor, right-click a node title and select Copy Encoded Node Tree.

This workflow is ideal for rapid iteration: tweak in the Node Editor, copy the string, paste into code.

Feature Scale and Frequency

Every noise generator that inherits from ScalableGenerator has a Feature Scale parameter (set via SetScale()). This controls the size of noise features in world units:

  • SetScale(100) - Each noise feature spans roughly 100 world units (default)
  • SetScale(50) - Features are half the size, more detailed
  • SetScale(200) - Features are twice as large, smoother

Feature Scale is the inverse of frequency: frequency = 1.0 / scale. If you're used to working with frequency, simply use SetScale(1.0f / yourFrequency).

This is separate from the step size in generation functions. Step size controls the sampling density, while Feature Scale controls the noise pattern itself.

Seeds

Seeds determine the random pattern of the noise. The same seed always produces the same output for the same position.

auto simplex = FastNoise::New<FastNoise::Simplex>();

// These produce different patterns:
simplex->GenSingle2D( 0, 0, 1337 );  // seed 1337
simplex->GenSingle2D( 0, 0, 42 );    // seed 42

Nodes that inherit from Seeded also have a Seed Offset (SetSeedOffset()), which is added to the generation seed for that node only. This is useful when you have multiple nodes of the same type and want different outputs without changing the global seed:

auto noise1 = FastNoise::New<FastNoise::Simplex>();
auto noise2 = FastNoise::New<FastNoise::Simplex>();
noise2->SetSeedOffset( 1 );  // now noise2 produces a different pattern from noise1

Thread Safety

All generation methods (GenUniformGrid*, GenPositionArray*, GenSingle*, GenTileable2D) are const and fully thread-safe. You can safely share a single node tree across multiple threads and call generation methods concurrently.

auto generator = FastNoise::New<FastNoise::Simplex>();

// Safe to do from multiple threads simultaneously:
// Thread 1: generator->GenUniformGrid2D( ... );
// Thread 2: generator->GenUniformGrid2D( ... );

However, do not modify node parameters (e.g. SetSource, SetScale) while generation is in progress on another thread.

Next Steps

Clone this wiki locally