diff --git a/README.md b/README.md
index 30525df3..e2f1ce05 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,7 @@ If all else failes, you can manually download the bundled file from the [release
 | **RGBSplitFilter**<br>_pixi-filters/rgb-split_<br>[View demo][RGBSplit_demo]                             | ![rgb split](https://filters.pixijs.download/main/screenshots/rgb.png?v=3)                           |                  |
 | **ShockwaveFilter**<br>_pixi-filters/shockwave_<br>[View demo][Shockwave_demo]           | ![shockwave](https://filters.pixijs.download/main/screenshots/shockwave.gif?v=3)         |
 | **SimpleLightmapFilter**<br>_pixi-filters/simple-lightmap_<br>[View demo][SimpleLightmap_demo]           | ![simple-lightmap](https://filters.pixijs.download/main/screenshots/simple-lightmap.png?v=3)         |
+| **SimplexNoiseFilter**<br>_pixi-filters/simplex-noise_<br>[View demo][SimplexNoise_demo]                 | ![simplex-noise](https://filters.pixijs.download/main/screenshots/simplex-noise.png?v=3)             |
 | **TiltShiftFilter**<br>_pixi-filters/tilt-shift_<br>[View demo][TiltShift_demo]                          | ![tilt-shift](https://filters.pixijs.download/main/screenshots/tilt-shift.png?v=3)                   |
 | **TwistFilter**<br>_pixi-filters/twist_<br>[View demo][Twist_demo]                                       | ![twist](https://filters.pixijs.download/main/screenshots/twist.png?v=3)                             |
 | **ZoomBlurFilter**<br>_pixi-filters/zoom-blur_<br>[View demo][ZoomBlur_demo]                             | ![zoom-blur](https://filters.pixijs.download/main/screenshots/zoom-blur.png?v=4)                     |
@@ -152,6 +153,7 @@ API documention can be found [here](http://pixijs.io/filters/docs/).
 [RGBSplit_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=RGBSplitFilter
 [Shockwave_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=ShockwaveFilter
 [SimpleLightmap_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=SimpleLightmapFilter
+[SimplexNoise_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=SimplexNoiseFilter
 [TiltShift_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=TiltShiftFilter
 [Twist_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=TwistFilter
 [ZoomBlur_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=ZoomBlurFilter
diff --git a/examples/src/filters/index.mjs b/examples/src/filters/index.mjs
index e294af18..7c3c42a5 100644
--- a/examples/src/filters/index.mjs
+++ b/examples/src/filters/index.mjs
@@ -38,6 +38,7 @@ export { default as reflection } from './reflection.mjs';
 export { default as rgb } from './rgb.mjs';
 export { default as shockwave } from './shockwave.mjs';
 export { default as simpleLightmap } from './lightmap.mjs';
+export { default as simplexNoise } from './simplex-noise.mjs';
 export { default as tiltShift } from './tilt-shift.mjs';
 export { default as twist } from './twist.mjs';
 export { default as zoomBlur } from './zoom-blur.mjs';
diff --git a/examples/src/filters/simplex-noise.mjs b/examples/src/filters/simplex-noise.mjs
new file mode 100644
index 00000000..53855a45
--- /dev/null
+++ b/examples/src/filters/simplex-noise.mjs
@@ -0,0 +1,16 @@
+export default function ()
+{
+    const app = this;
+
+    app.addFilter('SimplexNoiseFilter', {
+        oncreate(folder)
+        {
+            folder.add(this, 'strength', 0, 1).name('strength');
+            folder.add(this, 'noiseScale', 0, 50).name('noise scale');
+            folder.add(this, 'offsetX', 0, 5).name('offsetX');
+            folder.add(this, 'offsetY', 0, 5).name('offsetY');
+            folder.add(this, 'offsetZ', 0, 5).name('offsetZ');
+            folder.add(this, 'step', -1, 1).name('step');
+        },
+    });
+}
diff --git a/package.json b/package.json
index 8472e033..14adc066 100644
--- a/package.json
+++ b/package.json
@@ -168,6 +168,11 @@
       "require": "./lib/simple-lightmap/index.js",
       "types": "./lib/simple-lightmap/index.d.ts"
     },
+    "./simplex-noise": {
+      "import": "./lib/simplex-noise/index.mjs",
+      "require": "./lib/simplex-noise/index.js",
+      "types": "./lib/simplex-noise/index.d.ts"
+    },
     "./tilt-shift": {
       "import": "./lib/tilt-shift/index.mjs",
       "require": "./lib/tilt-shift/index.js",
diff --git a/scripts/screenshots/config.json b/scripts/screenshots/config.json
index 5d4ef067..c3d5623d 100644
--- a/scripts/screenshots/config.json
+++ b/scripts/screenshots/config.json
@@ -585,6 +585,14 @@
             "arguments": {
                 "strength": 4
             }
+        },
+        {
+            "name": "SimplexNoiseFilter",
+            "filename": "simplex-noise",
+            "arguments": {
+                "strength": 0.5,
+                "noiseScale": 10
+            }
         }
     ]
 }
diff --git a/src/index.ts b/src/index.ts
index 3a96e4b4..9fc52c18 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -32,6 +32,7 @@ export * from './reflection';
 export * from './rgb-split';
 export * from './shockwave';
 export * from './simple-lightmap';
+export * from './simplex-noise';
 export * from './tilt-shift';
 export * from './twist';
 export * from './zoom-blur';
diff --git a/src/simplex-noise/SimplexNoiseFilter.ts b/src/simplex-noise/SimplexNoiseFilter.ts
new file mode 100644
index 00000000..e02f339e
--- /dev/null
+++ b/src/simplex-noise/SimplexNoiseFilter.ts
@@ -0,0 +1,143 @@
+import { Filter, GlProgram, GpuProgram } from 'pixi.js';
+import { vertex, wgslVertex } from '../defaults';
+import fragment from './simplex.frag';
+import source from './simplex.wgsl';
+
+/** Options for the SimplexNoiseFilter constructor. */
+export interface SimplexNoiseFilterOptions
+{
+    /**
+     * Noise map strength.
+     * @default 0.5
+     */
+    strength?: number;
+    /**
+     * Noise map scale.
+     * @default 10.0
+     */
+    noiseScale?: number;
+    /**
+     * Horizontal offset for the noise map.
+     * @default 0
+     */
+    offsetX?: number;
+    /**
+     * Vertical offset for the noise map.
+     * @default 0
+     */
+    offsetY?: number;
+    /**
+     * Depth offset for the noise map.
+     * @default 0
+     */
+    offsetZ?: number;
+    /**
+     * The threshold used with the step function to create a blocky effect in the noise pattern.
+     * When this is greater than 0, the step function is used to compare the noise value to this threshold.
+     * @default -1
+     */
+    step?: number;
+}
+
+/**
+ * The SimplexNoiseFilter multiplies simplex noise with the current texture data. <br>
+ * ![original](../screenshots/original.png)![filter](../screenshots/simplex-noise.png)
+ * @class
+ * @extends Filter
+ * @see {@link https://www.npmjs.com/package/pixi-filters|pixi-filters}
+ */
+export class SimplexNoiseFilter extends Filter
+{
+    /** Default constructor options. */
+    public static readonly defaults: SimplexNoiseFilterOptions = {
+        strength: 0.5,
+        noiseScale: 10.0,
+        offsetX: 0,
+        offsetY: 0,
+        offsetZ: 0,
+        step: -1,
+    };
+
+    /**
+     * @param options - Options for the SimplexNoise constructor.
+     */
+    constructor(options?: SimplexNoiseFilterOptions)
+    {
+        options = { ...SimplexNoiseFilter.defaults, ...options };
+
+        const gpuProgram = GpuProgram.from({
+            vertex: {
+                source: wgslVertex,
+                entryPoint: 'mainVertex',
+            },
+            fragment: {
+                source,
+                entryPoint: 'mainFragment',
+            },
+        });
+
+        const glProgram = GlProgram.from({
+            vertex,
+            fragment,
+            name: 'simplex-filter',
+        });
+
+        super({
+            gpuProgram,
+            glProgram,
+            resources: {
+                simplexUniforms: {
+                    uStrength: { value: options?.strength ?? 0, type: 'f32' },
+                    uNoiseScale: { value: options?.noiseScale ?? 0, type: 'f32' },
+                    uOffsetX: { value: options?.offsetX ?? 0, type: 'f32' },
+                    uOffsetY: { value: options?.offsetY ?? 0, type: 'f32' },
+                    uOffsetZ: { value: options?.offsetZ ?? 0, type: 'f32' },
+                    uStep: { value: options?.step ?? 0, type: 'f32' },
+                }
+            }
+        });
+    }
+
+    /**
+     * Strength of the noise (color = (noiseMap + strength) * texture)
+     * @default 0.5
+     */
+    get strength(): number { return this.resources.simplexUniforms.uniforms.uStrength; }
+    set strength(value: number) { this.resources.simplexUniforms.uniforms.uStrength = value; }
+
+    /**
+     * Noise map scale.
+     * @default 10
+     */
+    get noiseScale(): number { return this.resources.simplexUniforms.uniforms.uNoiseScale; }
+    set noiseScale(value: number) { this.resources.simplexUniforms.uniforms.uNoiseScale = value; }
+
+    /**
+     * Horizontal offset for the noise map.
+     * @default 0
+     */
+    get offsetX(): number { return this.resources.simplexUniforms.uniforms.uOffsetX; }
+    set offsetX(value: number) { this.resources.simplexUniforms.uniforms.uOffsetX = value; }
+
+    /**
+     * Vertical offset for the noise map.
+     * @default 0
+     */
+    get offsetY(): number { return this.resources.simplexUniforms.uniforms.uOffsetY; }
+    set offsetY(value: number) { this.resources.simplexUniforms.uniforms.uOffsetY = value; }
+
+    /**
+     * Depth offset for the noise map.
+     * @default 0
+     */
+    get offsetZ(): number { return this.resources.simplexUniforms.uniforms.uOffsetZ; }
+    set offsetZ(value: number) { this.resources.simplexUniforms.uniforms.uOffsetZ = value; }
+
+    /**
+     * The threshold used with the step function to create a blocky effect in the noise pattern.
+     * When this is greater than 0, the step function is used to compare the noise value to this threshold.
+     * @default -1
+     */
+    get step(): number { return this.resources.simplexUniforms.uniforms.uStep; }
+    set step(value: number) { this.resources.simplexUniforms.uniforms.uStep = value; }
+}
diff --git a/src/simplex-noise/index.ts b/src/simplex-noise/index.ts
new file mode 100644
index 00000000..e79f99fd
--- /dev/null
+++ b/src/simplex-noise/index.ts
@@ -0,0 +1 @@
+export * from './SimplexNoiseFilter';
diff --git a/src/simplex-noise/simplex.frag b/src/simplex-noise/simplex.frag
new file mode 100644
index 00000000..827f4a36
--- /dev/null
+++ b/src/simplex-noise/simplex.frag
@@ -0,0 +1,61 @@
+precision highp float;
+in vec2 vTextureCoord;
+out vec4 finalColor;
+
+uniform sampler2D uTexture;
+uniform float uStrength;
+uniform float uNoiseScale;
+uniform float uOffsetX;
+uniform float uOffsetY;
+uniform float uOffsetZ;
+uniform float uStep;
+
+uniform vec4 uInputSize;
+uniform vec4 uInputClamp;
+
+//Noise from: https://www.shadertoy.com/view/4sc3z2
+const vec3 MOD3 = vec3(.1031,.11369,.13787);
+vec3 hash33(vec3 p3)
+{
+	p3 = fract(p3 * MOD3);
+    p3 += dot(p3, p3.yxz+19.19);
+    return -1.0 + 2.0 * fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));
+}
+
+float simplex_noise(vec3 p)
+{
+    const float K1 = 0.333333333;
+    const float K2 = 0.166666667;
+    
+    vec3 i = floor(p + (p.x + p.y + p.z) * K1);
+    vec3 d0 = p - (i - (i.x + i.y + i.z) * K2);
+    
+    vec3 e = step(vec3(0.0), d0 - d0.yzx);
+	vec3 i1 = e * (1.0 - e.zxy);
+	vec3 i2 = 1.0 - e.zxy * (1.0 - e);
+    
+    vec3 d1 = d0 - (i1 - 1.0 * K2);
+    vec3 d2 = d0 - (i2 - 2.0 * K2);
+    vec3 d3 = d0 - (1.0 - 3.0 * K2);
+    
+    vec4 h = max(0.6 - vec4(dot(d0, d0), dot(d1, d1), dot(d2, d2), dot(d3, d3)), 0.0);
+    vec4 n = h * h * h * h * vec4(dot(d0, hash33(i)), dot(d1, hash33(i + i1)), dot(d2, hash33(i + i2)), dot(d3, hash33(i + 1.0)));
+    
+    return dot(vec4(31.316), n);
+}
+
+void main(void)
+{
+    float noise = simplex_noise(
+                    vec3(vTextureCoord*uNoiseScale+vec2(uOffsetX, uOffsetY), uOffsetZ)
+                ) * 0.5 + 0.5;
+
+    noise += 2.0 * uStrength - 1.0;
+    noise = clamp(noise, 0.0, 1.0);
+
+    if (uStep > 0.0) {  //step > 0.5
+        noise = 1.0 - step(noise, uStep);
+    }
+
+    finalColor = texture(uTexture, vTextureCoord) * noise;
+}
diff --git a/src/simplex-noise/simplex.wgsl b/src/simplex-noise/simplex.wgsl
new file mode 100644
index 00000000..ac089c33
--- /dev/null
+++ b/src/simplex-noise/simplex.wgsl
@@ -0,0 +1,61 @@
+struct SimplexUniforms {
+  uStrength:f32,
+  uNoiseScale:f32,
+  uOffsetX:f32,
+  uOffsetY:f32,
+  uOffsetZ:f32,
+  uStep:f32
+};
+
+struct GlobalFilterUniforms {
+  uInputSize:vec4<f32>,
+  uInputPixel:vec4<f32>,
+  uInputClamp:vec4<f32>,
+  uOutputFrame:vec4<f32>,
+  uGlobalFrame:vec4<f32>,
+  uOutputTexture:vec4<f32>,
+};
+
+@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;
+
+@group(0) @binding(1) var uTexture: texture_2d<f32>; 
+@group(0) @binding(2) var uSampler: sampler;
+@group(1) @binding(0) var<uniform> simplexUniforms : SimplexUniforms;
+
+@fragment
+fn mainFragment(
+  @location(0) uv: vec2<f32>,
+  @builtin(position) position: vec4<f32>
+) -> @location(0) vec4<f32> {
+  var noise: f32 = simplex_noise(vec3<f32>(uv * simplexUniforms.uNoiseScale + vec2<f32>(simplexUniforms.uOffsetX, simplexUniforms.uOffsetY), simplexUniforms.uOffsetZ)) * 0.5 + 0.5;
+	noise = noise + (2. * simplexUniforms.uStrength - 1.);
+	noise = clamp(noise, 0.0, 1.0);
+	if (simplexUniforms.uStep > 0.0) {
+		noise = 1. - step(noise, simplexUniforms.uStep);
+	}
+	return textureSample(uTexture, uSampler, uv) * noise;
+}
+
+const MOD3: vec3<f32> = vec3<f32>(0.1031, 0.11369, 0.13787);
+fn hash33(p3: vec3<f32>) -> vec3<f32> {
+	var p3_var = p3;
+	p3_var = fract(p3_var * MOD3);
+	p3_var = p3_var + (dot(p3_var, p3_var.yxz + 19.19));
+	return -1. + 2. * fract(vec3<f32>((p3_var.x + p3_var.y) * p3_var.z, (p3_var.x + p3_var.z) * p3_var.y, (p3_var.y + p3_var.z) * p3_var.x));
+} 
+
+fn simplex_noise(p: vec3<f32>) -> f32 {
+	let K1: f32 = 0.33333334;
+	let K2: f32 = 0.16666667;
+	let i: vec3<f32> = floor(p + (p.x + p.y + p.z) * K1);
+	let d0: vec3<f32> = p - (i - (i.x + i.y + i.z) * K2);
+	let e: vec3<f32> = step(vec3<f32>(0.), d0 - d0.yzx);
+	let i1: vec3<f32> = e * (1. - e.zxy);
+	let i2: vec3<f32> = 1. - e.zxy * (1. - e);
+	let d1: vec3<f32> = d0 - (i1 - 1. * K2);
+	let d2: vec3<f32> = d0 - (i2 - 2. * K2);
+	let d3: vec3<f32> = d0 - (1. - 3. * K2);
+	let h: vec4<f32> = max(vec4<f32>(0.6) - vec4<f32>(dot(d0, d0), dot(d1, d1), dot(d2, d2), dot(d3, d3)), vec4<f32>(0.0));
+	let n: vec4<f32> = h * h * h * h * vec4<f32>(dot(d0, hash33(i)), dot(d1, hash33(i + i1)), dot(d2, hash33(i + i2)), dot(d3, hash33(i + 1.)));
+	return dot(vec4<f32>(31.316), n);
+} 
\ No newline at end of file