Examples
generic fbm-clouds.ts
fBm clouds
Fractal Brownian motion — value noise summed over octaves of doubling frequency / halving amplitude, drifting over time. The classic procedural cloud field, octave count live-tunable.
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 — fBm clouds (fractal value noise) ═══//// Value-noise summed over octaves (fractal Brownian motion): each octave doubles// the frequency and halves the amplitude, drifting over time. The textbook// procedural-cloud / terrain primitive. Showcases helper fns + a `Loop`-driven// octave accumulator. One DSL source → WGSL (WebGPU) + GLSL ES 3.00 (WebGL2).
import { fn, module, u32, f32, toF32, vec2, vec3, vec4, sin, floor, fract, dot, mix, clamp, Loop, Let, f32T, vec2fT,} from '../src/index.ts'import { VsOut, vs, fullscreenUniforms } from './_fullscreen.ts'import type { ShaderExample } from './_shared.ts'const U = fullscreenUniforms({ octaves: 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 weights.const 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))) // 3f² − 2f³ 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, )})
const fs = fn( 'fs', { vo: VsOut }, ({ vo }) => { const uv = vo.uv const q = vec2(uv.x.mul(3), uv.y.mul(3)) // mutable sample point (octave frequency) const v = f32(0) const amp = f32(0.55) Loop( u32(0), (i) => toF32(i).lt(U.field.octaves), () => { v.assign(v.add(amp.mul(noise({ p: q.add(vec2(U.field.time.mul(0.08), 0)) })))) q.assign(q.mul(2.02)) amp.assign(amp.mul(0.5)) }, ) // sky → cloud ramp const sky = vec3(0.2, 0.42, 0.72) const cloud = vec3(0.97, 0.97, 1.0) return vec4(mix(sky, cloud, clamp(v, 0, 1)), 1) }, { stage: 'fragment', retAttr: '@location(0)' },)
// `hash`/`noise` are called via their handles, so module() collects them transitively — funcs lists only the entry points.const fbmModule = module({ structs: [U.struct, VsOut.decl], bindings: [U.binding], funcs: [vs, fs],})
export const fbmClouds: ShaderExample = { id: 'fbm-clouds', title: 'fBm clouds', blurb: 'Fractal Brownian motion — value noise summed over octaves of doubling frequency / halving amplitude, drifting over time. The classic procedural cloud field, octave count live-tunable.', category: 'generic', file: 'fbm-clouds.ts', module: fbmModule, renderable: true, controls: { time: { kind: 'time' }, resolution: { kind: 'resolution' }, octaves: { kind: 'slider', label: 'Octaves', min: 1, max: 8, step: 1, value: 6 }, },}struct Uniforms { time: f32, resolution: vec2<f32>, octaves: 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);}
@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 _licm0 = U.octaves; let _licm1 = vec2<f32>((U.time * 0.08), 0.0); var _av0: f32 = 0.0; var _av1: vec2<f32> = vec2<f32>((vo.uv.x * 3.0), (vo.uv.y * 3.0)); var _av2: f32 = 0.55; for (var _v0: u32 = 0u; (f32(_v0) < _licm0); _v0 = (_v0 + 1u)) { _av0 = (_av0 + (_av2 * noise((_av1 + _licm1)))); _av1 = (_av1 * 2.02); _av2 = (_av2 * 0.5); } return vec4<f32>(mix(vec3<f32>(0.2, 0.42, 0.72), vec3<f32>(0.97, 0.97, 1.0), clamp(_av0, 0.0, 1.0)), 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 octaves;} U;float hash(vec2 p);float noise(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);}in vec2 uv;layout(location = 0) out vec4 _ret;
vec4 fs_impl(VsOut vo) { float _licm0 = U.octaves; vec2 _licm1 = vec2((U.time * 0.08), 0.0); float _av0 = 0.0; vec2 _av1 = vec2((vo.uv.x * 3.0), (vo.uv.y * 3.0)); float _av2 = 0.55; for (uint _v0 = 0u; (float(_v0) < _licm0); _v0 = (_v0 + 1u)) { _av0 = (_av0 + (_av2 * noise((_av1 + _licm1)))); _av1 = (_av1 * 2.02); _av2 = (_av2 * 0.5); } return vec4(mix(vec3(0.2, 0.42, 0.72), vec3(0.97, 0.97, 1.0), clamp(_av0, 0.0, 1.0)), 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": "octaves", "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 fbm-clouds
— prints the WGSL, GLSL, reflection. Source:
GitHub.
New to the IR? See Concepts; the full authoring + emit surface is in the API reference.