diff --git a/SharpEngine.Core.Numerics/MathExtensions.cs b/SharpEngine.Core.Numerics/MathExtensions.cs
new file mode 100644
index 0000000..21881ae
--- /dev/null
+++ b/SharpEngine.Core.Numerics/MathExtensions.cs
@@ -0,0 +1,64 @@
+namespace SharpEngine.Core.Numerics;
+
+///
+/// Provides mathematical utility functions.
+///
+public static partial class MathExtensions
+{
+ ///
+ /// Returns the largest integer less than or equal to the specified float value.
+ ///
+ /// The float value.
+ /// The largest integer less than or equal to the specified float value.
+ public static int FastFloor(float value)
+ {
+ int integer = (int)value;
+
+ return value < integer ? integer - 1 : integer;
+ }
+
+ ///
+ /// Smooths the input value.
+ ///
+ /// The input value.
+ /// The smoothed value.
+ public static float Fade(float t)
+ {
+ return t * t * t * (t * (t * 6f - 15f) + 10f);
+ }
+
+ ///
+ /// Performs linear interpolation between two values.
+ ///
+ /// The start value.
+ /// The end value.
+ /// The interpolation factor.
+ /// The interpolated value.
+ public static float Lerp(float a, float b, float t)
+ {
+ return a + t * (b - a);
+ }
+
+ ///
+ /// Computes the gradient for a given hash and coordinates.
+ ///
+ /// The hash value.
+ /// The x coordinate.
+ /// The y coordinate.
+ /// The z coordinate.
+ /// The computed gradient.
+ public static float Grad(int hash, float x, float y, float z)
+ {
+ int h = hash & 15;
+
+ float u = h < 8 ? x : y;
+ float v = h < 4
+ ? y
+ : h is 12 or 14
+ ? x
+ : z;
+
+ return ((h & 1) == 0 ? u : -u) +
+ ((h & 2) == 0 ? v : -v);
+ }
+}
diff --git a/SharpEngine.Core.Numerics/Noise/INoiseGenerator.cs b/SharpEngine.Core.Numerics/Noise/INoiseGenerator.cs
new file mode 100644
index 0000000..d285dc2
--- /dev/null
+++ b/SharpEngine.Core.Numerics/Noise/INoiseGenerator.cs
@@ -0,0 +1,45 @@
+namespace SharpEngine.Core.Numerics.Noise;
+
+///
+/// Defines a noise generator with controllable fractal parameters.
+///
+public interface INoiseGenerator
+{
+ /// Gets or sets the scale factor applied to the input coordinates.
+ ///
+ /// Higher values make the noise appear more zoomed out.
+ ///
+ float Scale { get; set; }
+
+ /// Gets or sets the number of octaves (noise layers) to combine.
+ int Octaves { get; set; }
+
+ ///
+ /// Gets or sets the persistence,
+ /// which controls how much each octave contributes relative to the previous one (amplitude decay per octave).
+ ///
+ float Persistence { get; set; }
+
+ ///
+ /// Gets or sets the lacunarity,
+ /// which controls how quickly the frequency increases per octave.
+ ///
+ float Lacunarity { get; set; }
+
+ ///
+ /// Samples the noise at the given 2D coordinates.
+ ///
+ /// The X coordinate.
+ /// The Z coordinate.
+ /// A normalized value in the range [0, 1].
+ float Sample2D(float x, float z);
+
+ ///
+ /// Samples the noise at the given 3D coordinates.
+ ///
+ /// The X coordinate.
+ /// The Y coordinate.
+ /// The Z coordinate.
+ /// A normalized value in the range [0, 1].
+ float Sample3D(float x, float y, float z);
+}
diff --git a/SharpEngine.Core.Numerics/Noise/PerlinNoiseGenerator.cs b/SharpEngine.Core.Numerics/Noise/PerlinNoiseGenerator.cs
new file mode 100644
index 0000000..a961cd8
--- /dev/null
+++ b/SharpEngine.Core.Numerics/Noise/PerlinNoiseGenerator.cs
@@ -0,0 +1,191 @@
+using MathEx = SharpEngine.Core.Numerics.MathExtensions;
+
+namespace SharpEngine.Core.Numerics.Noise;
+
+///
+/// Deterministic Perlin noise generator with fractal octave support.
+///
+public sealed class PerlinNoiseGenerator : INoiseGenerator
+{
+ private readonly int[] _permutation = new int[512];
+
+ private float _scale = 32f;
+ private int _octaves = 4;
+ private float _persistence = 0.5f;
+ private float _lacunarity = 2f;
+
+ ///
+ /// Creates a new Perlin noise generator.
+ ///
+ /// The seed used to generate the permutation table.
+ public PerlinNoiseGenerator(int seed = 1337)
+ {
+ InitializePermutation(seed);
+ }
+
+ ///
+ public float Scale
+ {
+ get => _scale;
+ set => _scale = value <= 0f ? 0.0001f : value;
+ }
+
+ ///
+ public int Octaves
+ {
+ get => _octaves;
+ set => _octaves = Math.Max(1, value);
+ }
+
+ ///
+ public float Persistence
+ {
+ get => _persistence;
+ set => _persistence = Math.Max(0f, value);
+ }
+
+ ///
+ public float Lacunarity
+ {
+ get => _lacunarity;
+ set => _lacunarity = Math.Max(0.0001f, value);
+ }
+
+ ///
+ public float Sample2D(float x, float z)
+ {
+ float total = 0f;
+ float amplitude = 1f;
+ float frequency = 1f;
+ float maxAmplitude = 0f;
+
+ for (int i = 0; i < Octaves; i++)
+ {
+ float sampleX = x / Scale * frequency;
+ float sampleZ = z / Scale * frequency;
+
+ float noise = Perlin(sampleX, 0f, sampleZ);
+
+ total += noise * amplitude;
+ maxAmplitude += amplitude;
+
+ amplitude *= Persistence;
+ frequency *= Lacunarity;
+ }
+
+ float normalized = total / maxAmplitude;
+
+ return ToUnitRange(normalized);
+ }
+
+ ///
+ public float Sample3D(float x, float y, float z)
+ {
+ float total = 0f;
+ float amplitude = 1f;
+ float frequency = 1f;
+ float maxAmplitude = 0f;
+
+ for (int i = 0; i < Octaves; i++)
+ {
+ float sampleX = x / Scale * frequency;
+ float sampleY = y / Scale * frequency;
+ float sampleZ = z / Scale * frequency;
+
+ float noise = Perlin(sampleX, sampleY, sampleZ);
+
+ total += noise * amplitude;
+ maxAmplitude += amplitude;
+
+ amplitude *= Persistence;
+ frequency *= Lacunarity;
+ }
+
+ float normalized = total / maxAmplitude;
+
+ return ToUnitRange(normalized);
+ }
+
+ private void InitializePermutation(int seed)
+ {
+ int[] source = new int[256];
+
+ for (int i = 0; i < source.Length; i++)
+ source[i] = i;
+
+ Random random = new(seed);
+
+ for (int i = source.Length - 1; i > 0; i--)
+ {
+ int swapIndex = random.Next(i + 1);
+
+ (source[i], source[swapIndex]) = (source[swapIndex], source[i]);
+ }
+
+ for (int i = 0; i < 512; i++)
+ _permutation[i] = source[i & 255];
+ }
+
+ private float Perlin(float x, float y, float z)
+ {
+ int xi = MathEx.FastFloor(x) & 255;
+ int yi = MathEx.FastFloor(y) & 255;
+ int zi = MathEx.FastFloor(z) & 255;
+
+ float xf = x - MathEx.FastFloor(x);
+ float yf = y - MathEx.FastFloor(y);
+ float zf = z - MathEx.FastFloor(z);
+
+ float u = MathEx.Fade(xf);
+ float v = MathEx.Fade(yf);
+ float w = MathEx.Fade(zf);
+
+ var yiOffset = _permutation[xi] + yi;
+
+ int aaa = _permutation[_permutation[yiOffset] + zi];
+ int aba = _permutation[_permutation[yiOffset + 1] + zi];
+ int aab = _permutation[_permutation[yiOffset] + zi + 1];
+ int abb = _permutation[_permutation[yiOffset + 1] + zi + 1];
+
+ var yiOffsetNext = _permutation[xi + 1] + yi;
+
+ int baa = _permutation[_permutation[yiOffsetNext] + zi];
+ int bba = _permutation[_permutation[yiOffsetNext + 1] + zi];
+ int bab = _permutation[_permutation[yiOffsetNext] + zi + 1];
+ int bbb = _permutation[_permutation[yiOffsetNext + 1] + zi + 1];
+
+ float x1 = MathEx.Lerp(
+ MathEx.Grad(aaa, xf, yf, zf),
+ MathEx.Grad(baa, xf - 1f, yf, zf),
+ u);
+
+ float x2 = MathEx.Lerp(
+ MathEx.Grad(aba, xf, yf - 1f, zf),
+ MathEx.Grad(bba, xf - 1f, yf - 1f, zf),
+ u);
+
+ float y1 = MathEx.Lerp(x1, x2, v);
+
+ x1 = MathEx.Lerp(
+ MathEx.Grad(aab, xf, yf, zf - 1f),
+ MathEx.Grad(bab, xf - 1f, yf, zf - 1f),
+ u);
+
+ x2 = MathEx.Lerp(
+ MathEx.Grad(abb, xf, yf - 1f, zf - 1f),
+ MathEx.Grad(bbb, xf - 1f, yf - 1f, zf - 1f),
+ u);
+
+ float y2 = MathEx.Lerp(x1, x2, v);
+
+ return MathEx.Lerp(y1, y2, w);
+ }
+
+ private static float ToUnitRange(float value)
+ {
+ // Perlin is roughly in [-1, 1], so remap to [0, 1].
+ float result = value * 0.5f + 0.5f;
+
+ return Math.Clamp(result, 0f, 1f);
+ }
+}
\ No newline at end of file