Tri-Noise 3D
Triangle-wave 3D noise (WGSL + CPU)
Quick Start
import { triNoise3DWGSL, triNoise3D } from './webgpu-market/tri-noise-3d/tri-noise-3d';
// In a shader — prepend the WGSL source
const shader = triNoise3DWGSL + `
@fragment fn main(@location(0) uv: vec2f) -> @location(0) vec4f {
let value = triNoise3D(vec3f(uv * 4.0, 0.0), time);
return vec4f(vec3f(value), 1.0);
}
`;
// On the CPU — same function, same output
const value = triNoise3D([x, y, z], elapsed); Source
// Triangle-wave 3D noise
// Ported from https://github.com/cabbibo/glsl-tri-noise-3d
// Returns a value roughly in [0, 1].
fn tri(x: f32) -> f32 {
return abs(fract(x) - 0.5);
}
fn tri3(p: vec3f) -> vec3f {
return vec3f(
tri(p.z + tri(p.y)),
tri(p.z + tri(p.x)),
tri(p.y + tri(p.x))
);
}
// position : 3D sample point
// t : time offset for animation
fn triNoise3D(position: vec3f, t: f32) -> f32 {
var p = position;
var z = 1.4;
var rz = 0.0;
var bp = position;
for (var i = 0; i <= 3; i++) {
let dg = tri3(bp * 2.0);
p = p + dg + vec3f(t * 0.1);
bp = bp * 1.8;
z = z * 1.5;
p = p * 1.2;
let s = tri(p.z + tri(p.x + tri(p.y)));
rz = rz + s / z;
bp = bp + vec3f(0.14);
}
return rz;
}
import wgslSource from './tri-noise-3d.wgsl?raw';
/**
* WGSL source string for the triNoise3D functions (`tri`, `tri3`, `triNoise3D`).
* Paste this into your shader before using `triNoise3D(position, t)`.
*/
export const triNoise3DWGSL: string = wgslSource;
/** A simple [x, y, z] tuple used by the CPU implementation. */
export type Vec3 = readonly [number, number, number];
// --- CPU implementation (mirrors the WGSL exactly) ---
function fract(x: number): number {
return x - Math.floor(x);
}
function tri(x: number): number {
return Math.abs(fract(x) - 0.5);
}
function tri3(p: Vec3): [number, number, number] {
return [tri(p[2] + tri(p[1])), tri(p[2] + tri(p[0])), tri(p[1] + tri(p[0]))];
}
/**
* Triangle-wave 3D noise — CPU/TypeScript implementation.
* Matches the output of the WGSL `triNoise3D` function.
*
* @param position 3D sample point
* @param time Time offset for animation
* @returns Noise value roughly in [0, 1]
*/
export function triNoise3D(position: Vec3, time: number): number {
let p: [number, number, number] = [position[0], position[1], position[2]];
let z = 1.4;
let rz = 0.0;
let bp: [number, number, number] = [position[0], position[1], position[2]];
for (let i = 0; i <= 3; i++) {
const dg = tri3([bp[0] * 2, bp[1] * 2, bp[2] * 2]);
const dt = time * 0.1;
p = [p[0] + dg[0] + dt, p[1] + dg[1] + dt, p[2] + dg[2] + dt];
bp = [bp[0] * 1.8, bp[1] * 1.8, bp[2] * 1.8];
z *= 1.5;
p = [p[0] * 1.2, p[1] * 1.2, p[2] * 1.2];
const s = tri(p[2] + tri(p[0] + tri(p[1])));
rz += s / z;
bp = [bp[0] + 0.14, bp[1] + 0.14, bp[2] + 0.14];
}
return rz;
}
Documentation
tri-noise-3d
Triangle-wave 3D noise — a WGSL function you embed in your own shader, plus a matching CPU/TypeScript implementation that produces the same values.
API
triNoise3DWGSL: string
Raw WGSL source defining three functions: tri, tri3, and triNoise3D. Paste or concatenate this into your shader module.
triNoise3D(position, time): number
CPU implementation. Returns a noise value roughly in [0, 1].
position—[x, y, z]sample pointtime— Time offset for animation
Further Reading
Further Reading
Rationale
Not all noise needs to be Perlin or Simplex. Triangle-wave noise produces a distinct organic, rippling quality — less "cloudy" than classic gradient noise, more like flowing liquid or heat distortion. It's cheap to compute (no gradient lookups or permutation tables) and works well for animated displacement, water caustics, and abstract procedural textures.
Original Source
- cabbibo/glsl-tri-noise-3d — The original GLSL implementation this module is ported from. Isaac Cohen (cabbibo) is known for creative coding and procedural art. https://github.com/cabbibo/glsl-tri-noise-3d
How It Works
The algorithm composes triangle waves (abs(fract(x) - 0.5)) in 3D across multiple octaves. Each octave:
- Samples a triangle wave of the current position, feeding each axis into the next (creating cross-axis dependency).
- Adds a time-based offset for animation.
- Scales the position up (increasing frequency) and scales the contribution down (decreasing amplitude).
This produces fractal-like detail without any hash tables or gradient vectors — just fract, abs, and arithmetic.
Further Learning
- The Book of Shaders: Noise — Patricio Gonzalez Vivo's chapter on noise covers the design space beyond Perlin/Simplex, including wave-based approaches. https://thebookofshaders.com/11/
- Inigo Quilez: Useful Little Functions — A collection of small mathematical building blocks for procedural graphics, including triangle wave patterns. https://iquilezles.org/articles/functions/