Examples
generic domain-warp.ts
Domain warping
fbm fed its own output — f(p) = fbm(p + w·fbm(p + fbm(p))) — melts lattice noise into marbled organic flow; the intermediate warp vectors double as colour axes. Warp strength is live.
Live
0.0s
One source, compiled every way
The DSL source below, compiled to WebGPU (WGSL), WebGL2 (GLSL ES 3.00 — two stages), and a reflection JSON the host binds from. Nothing but the DSL tab is hand-written.
// ═══ @xgis/shader-dsl example — domain warping (fbm of fbm of fbm) ═══//// Inigo Quilez's domain-warping construction: instead of colouring by// fbm(p) directly, feed noise its own output — f(p) = fbm(p + w·fbm(p + fbm(p)))// — and the lattice melts into marbled, organic flow. The intermediate warp// vectors q and r double as colour axes. The fbm helper here is a 4-octave// UNROLLED expression (each octave doubles frequency, halves amplitude), so// it stays a pure value function. WGSL (WebGPU) + GLSL ES 3.00 (WebGL2).
import { fn, module, vec2, vec3, vec4, sin, floor, fract, dot, mix, length, clamp, smoothstep, Let, f32T, vec2fT,} from '../src/index.ts'import { VsOut, vs, fullscreenUniforms, screenCoords } from './_fullscreen.ts'import type { ShaderExample } from './_shared.ts'const U = fullscreenUniforms({ warp: f32T })
// scalar hash of a lattice point → [0,1)const hash = fn('hash', { p: vec2fT }, ({ p }) => fract(sin(dot(p, vec2(127.1, 311.7))).mul(43758.5453)),)
// bilinear value noise with smootherstep weightsconst noise = fn('noise', { p: vec2fT }, ({ p }) => { const i = Let(floor(p)) const f = Let(fract(p)) const u = f.mul(f).mul(vec2(3).sub(f.mul(2))) return mix( mix(hash({ p: i }), hash({ p: i.add(vec2(1, 0)) }), u.x), mix(hash({ p: i.add(vec2(0, 1)) }), hash({ p: i.add(vec2(1, 1)) }), u.x), u.y, )})
// 4-octave fbm, unrolled so the helper stays a pure value expressionconst fbm = fn('fbm', { p: vec2fT }, ({ p }) => { return noise({ p }) .mul(0.5) .add(noise({ p: p.mul(2.02) }).mul(0.25)) .add(noise({ p: p.mul(4.08) }).mul(0.125)) .add(noise({ p: p.mul(8.2) }).mul(0.0625))})
const fs = fn( 'fs', { vo: VsOut }, ({ vo }) => { const t = U.field.time const res = U.field.resolution const p = screenCoords(vo.uv, res).mul(1.8) const w = U.field.warp // first warp: q = (fbm(p), fbm(p + k₁)) const q = Let(vec2(fbm({ p }), fbm({ p: p.add(vec2(5.2, 1.3)) }))) // second warp: r = (fbm(p + w·q + k₂ + drift), fbm(p + w·q + k₃)) const pq = p.add(q.mul(w)) const r = Let( vec2( fbm({ p: pq.add(vec2(1.7, 9.2)).add(vec2(t.mul(0.15), t.mul(0.12))) }), fbm({ p: pq.add(vec2(8.3, 2.8)) }), ), ) // final field const f = Let(fbm({ p: p.add(r.mul(w)) })) // colour: base by f², tinted by the warp magnitudes (iq's construction) const a = mix(vec3(0.09, 0.12, 0.2), vec3(0.85, 0.83, 0.72), clamp(f.mul(f).mul(2.8), 0, 1)) const b = mix(a, vec3(0.2, 0.5, 0.55), clamp(length(q).mul(0.9), 0, 1)) const c = mix(b, vec3(0.66, 0.3, 0.2), clamp(smoothstep(0.4, 1, r.y).mul(0.6), 0, 1)) return vec4(c.mul(f.mul(1.4).add(0.35)), 1) }, { stage: 'fragment', retAttr: '@location(0)' },)
const domainWarpModule = module({ structs: [U.struct, VsOut.decl], bindings: [U.binding], funcs: [vs, fs],})
export const domainWarp: ShaderExample = { id: 'domain-warp', title: 'Domain warping', blurb: 'fbm fed its own output — f(p) = fbm(p + w·fbm(p + fbm(p))) — melts lattice noise into marbled organic flow; the intermediate warp vectors double as colour axes. Warp strength is live.', category: 'generic', file: 'domain-warp.ts', module: domainWarpModule, renderable: true, controls: { time: { kind: 'time' }, resolution: { kind: 'resolution' }, warp: { kind: 'slider', label: 'Warp', min: 0, max: 4, step: 0.1, value: 2.4 }, },}struct Uniforms { time: f32, resolution: vec2<f32>, warp: f32,}
struct VsOut { @builtin(position) pos: vec4<f32>, @location(0) uv: vec2<f32>,}
@group(0) @binding(0) var<uniform> U: Uniforms;
fn hash(p: vec2<f32>) -> f32 { return fract((sin(dot(p, vec2<f32>(127.1, 311.7))) * 43758.5453));}
fn noise(p: vec2<f32>) -> f32 { let _cse0 = vec2<f32>(3.0); let _v0 = floor(p); let _v1 = fract(p); let _lc0 = ((_v1 * _v1) * (_cse0 - (_v1 * 2.0))).x; return mix(mix(hash(_v0), hash((_v0 + vec2<f32>(1.0, 0.0))), _lc0), mix(hash((_v0 + vec2<f32>(0.0, 1.0))), hash((_v0 + vec2<f32>(1.0, 1.0))), _lc0), ((_v1 * _v1) * (_cse0 - (_v1 * 2.0))).y);}
fn fbm(p: vec2<f32>) -> f32 { return ((((noise(p) * 0.5) + (noise((p * 2.02)) * 0.25)) + (noise((p * 4.08)) * 0.125)) + (noise((p * 8.2)) * 0.0625));}
@vertexfn vs(@builtin(vertex_index) vi: u32) -> VsOut { let _cse0 = ((f32((vi & 1u)) * 4.0) - 1.0); let _cse1 = ((f32((vi >> 1u)) * 4.0) - 1.0); return VsOut(vec4<f32>(_cse0, _cse1, 0.0, 1.0), vec2<f32>(((_cse0 * 0.5) + 0.5), ((_cse1 * 0.5) + 0.5)));}
@fragmentfn fs(vo: VsOut) -> @location(0) vec4<f32> { let _cse0 = (vec2<f32>((((vo.uv.x * 2.0) - 1.0) * (U.resolution.x / U.resolution.y)), ((vo.uv.y * 2.0) - 1.0)) * 1.8); let _v0 = vec2<f32>(fbm(_cse0), fbm((_cse0 + vec2<f32>(5.2, 1.3)))); let _lc0 = (_cse0 + (_v0 * U.warp)); let _v1 = vec2<f32>(fbm(((_lc0 + vec2<f32>(1.7, 9.2)) + vec2<f32>((U.time * 0.15), (U.time * 0.12)))), fbm((_lc0 + vec2<f32>(8.3, 2.8)))); let _v2 = fbm((_cse0 + (_v1 * U.warp))); return vec4<f32>((mix(mix(mix(vec3<f32>(0.09, 0.12, 0.2), vec3<f32>(0.85, 0.83, 0.72), clamp(((_v2 * _v2) * 2.8), 0.0, 1.0)), vec3<f32>(0.2, 0.5, 0.55), clamp((length(_v0) * 0.9), 0.0, 1.0)), vec3<f32>(0.66, 0.3, 0.2), clamp((smoothstep(0.4, 1.0, _v1.y) * 0.6), 0.0, 1.0)) * ((_v2 * 1.4) + 0.35)), 1.0);}#version 300 esprecision highp float;precision highp int;
struct VsOut { vec4 pos; vec2 uv;};out vec2 uv;
VsOut vs_impl(uint vi) { float _cse0 = ((float((vi & 1u)) * 4.0) - 1.0); float _cse1 = ((float((vi >> 1u)) * 4.0) - 1.0); return VsOut(vec4(_cse0, _cse1, 0.0, 1.0), vec2(((_cse0 * 0.5) + 0.5), ((_cse1 * 0.5) + 0.5)));}
void main() { VsOut _out = vs_impl(uint(gl_VertexID)); gl_Position = _out.pos; uv = _out.uv;}#version 300 esprecision highp float;precision highp int;
struct VsOut { vec4 pos; vec2 uv;};layout(std140) uniform Uniforms { float time; vec2 resolution; float warp;} U;float hash(vec2 p);float noise(vec2 p);float fbm(vec2 p);float hash(vec2 p) { return fract((sin(dot(p, vec2(127.1, 311.7))) * 43758.5453));}
float noise(vec2 p) { vec2 _cse0 = vec2(3.0); vec2 _v0 = floor(p); vec2 _v1 = fract(p); float _lc0 = ((_v1 * _v1) * (_cse0 - (_v1 * 2.0))).x; return mix(mix(hash(_v0), hash((_v0 + vec2(1.0, 0.0))), _lc0), mix(hash((_v0 + vec2(0.0, 1.0))), hash((_v0 + vec2(1.0, 1.0))), _lc0), ((_v1 * _v1) * (_cse0 - (_v1 * 2.0))).y);}
float fbm(vec2 p) { return ((((noise(p) * 0.5) + (noise((p * 2.02)) * 0.25)) + (noise((p * 4.08)) * 0.125)) + (noise((p * 8.2)) * 0.0625));}in vec2 uv;layout(location = 0) out vec4 _ret;
vec4 fs_impl(VsOut vo) { vec2 _cse0 = (vec2((((vo.uv.x * 2.0) - 1.0) * (U.resolution.x / U.resolution.y)), ((vo.uv.y * 2.0) - 1.0)) * 1.8); vec2 _v0 = vec2(fbm(_cse0), fbm((_cse0 + vec2(5.2, 1.3)))); vec2 _lc0 = (_cse0 + (_v0 * U.warp)); vec2 _v1 = vec2(fbm(((_lc0 + vec2(1.7, 9.2)) + vec2((U.time * 0.15), (U.time * 0.12)))), fbm((_lc0 + vec2(8.3, 2.8)))); float _v2 = fbm((_cse0 + (_v1 * U.warp))); return vec4((mix(mix(mix(vec3(0.09, 0.12, 0.2), vec3(0.85, 0.83, 0.72), clamp(((_v2 * _v2) * 2.8), 0.0, 1.0)), vec3(0.2, 0.5, 0.55), clamp((length(_v0) * 0.9), 0.0, 1.0)), vec3(0.66, 0.3, 0.2), clamp((smoothstep(0.4, 1.0, _v1.y) * 0.6), 0.0, 1.0)) * ((_v2 * 1.4) + 0.35)), 1.0);}
void main() { VsOut vo; vo.pos = gl_FragCoord; vo.uv = uv; _ret = fs_impl(vo);}{ "bindGroups": [ { "group": 0, "entries": [ { "group": 0, "binding": 0, "name": "U", "space": "uniform", "resourceKind": "uniform-buffer", "structName": "Uniforms" } ] } ], "uniforms": [ { "name": "Uniforms", "size": 32, "align": 16, "fields": [ { "name": "time", "type": "f32", "offset": 0, "align": 4, "size": 4 }, { "name": "resolution", "type": "vec2<f32>", "offset": 8, "align": 8, "size": 8 }, { "name": "warp", "type": "f32", "offset": 16, "align": 4, "size": 4 } ] } ], "storage": [], "entries": [ { "name": "vs", "stage": "vertex", "inputs": [ "u32" ], "output": "struct:VsOut" }, { "name": "fs", "stage": "fragment", "inputs": [ "struct:VsOut" ], "output": "vec4<f32>" } ], "overrides": []}
Run it locally:
npx tsx examples/print.ts domain-warp
— prints the WGSL, GLSL, reflection. Source:
GitHub.
New to the IR? See Concepts; the full authoring + emit surface is in the API reference.