Examples
generic kaleidoscope.ts
Kaleidoscope
The polar mirror fold — the angle floor-modded into one sector and mirrored about its midline, so every sector repeats the same wedge of swirling fbm and rings. Segment count 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 — kaleidoscope (polar mirror fold) ═══//// The kaleidoscope fold: convert to polar, floor-mod the angle into one// sector, mirror about the sector's midline, convert back — every sector now// shows the same wedge of pattern, seamlessly. The pattern inside the wedge is// swirling fbm + concentric rings through a cosine palette. The fold uses// `mod` (#839), the portable FLOOR-mod, so the negative angles atan2// produces wrap identically on both targets. WGSL + GLSL ES 3.00 (WebGL2).
import { fn, module, f32, vec2, vec3, vec4, sin, cos, floor, fract, dot, mix, abs, mod, length, atan2, 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({ segments: 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 palette = fn('palette', { t: f32T }, ({ t }) => { const ph = vec3(0.0, 0.33, 0.67) return vec3(0.5).add(cos(t.add(ph).mul(6.283)).mul(0.5))})
const fs = fn( 'fs', { vo: VsOut }, ({ vo }) => { const t = U.field.time const res = U.field.resolution const p = screenCoords(vo.uv, res) const r = Let(length(p)) const a0 = Let(atan2(p.y, p.x)) // fold: floor-mod the angle into one sector, mirror about its midline const sector = Let(f32(6.2831853).div(U.field.segments)) const am = mod(a0, sector) const af = Let(abs(am.sub(sector.mul(0.5)))) const q = vec2(cos(af), sin(af)).mul(r) // wedge pattern: swirling fbm + concentric rings const v = Let(fbm({ p: q.mul(3).add(vec2(t.mul(0.12), t.mul(0.09).neg())) })) const rings = sin(r.mul(9).sub(t.mul(0.8))) .mul(0.5) .add(0.5) const col = palette({ t: v.mul(0.7).add(rings.mul(0.15)).add(r.mul(0.3)).sub(t.mul(0.03)) }) // the fbm field doubles as a brightness relief so the wedges keep depth const relief = v.mul(0.9).add(0.35) // vignette so the fold's outer edge fades instead of clipping const vig = f32(1).sub(smoothstep(0.55, 1.25, r)) return vec4(col.mul(relief).mul(vig), 1) }, { stage: 'fragment', retAttr: '@location(0)' },)
const kaleidoscopeModule = module({ structs: [U.struct, VsOut.decl], bindings: [U.binding], funcs: [vs, fs],})
export const kaleidoscope: ShaderExample = { id: 'kaleidoscope', title: 'Kaleidoscope', blurb: 'The polar mirror fold — the angle floor-modded into one sector and mirrored about its midline, so every sector repeats the same wedge of swirling fbm and rings. Segment count is live.', category: 'generic', file: 'kaleidoscope.ts', module: kaleidoscopeModule, renderable: true, controls: { time: { kind: 'time' }, resolution: { kind: 'resolution' }, segments: { kind: 'slider', label: 'Segments', min: 3, max: 12, step: 1, value: 6 }, },}struct Uniforms { time: f32, resolution: vec2<f32>, segments: 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));}
fn palette(t: f32) -> vec3<f32> { return (vec3<f32>(0.5) + (cos(((t + vec3<f32>(0.0, 0.33, 0.67)) * 6.283)) * 0.5));}
@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)); let _v0 = length(_cse0); let _v1 = atan2(_cse0.y, _cse0.x); let _v2 = (6.2831853 / U.segments); let _v3 = abs(((_v1 - _v2 * floor(_v1 / _v2)) - (_v2 * 0.5))); let _v4 = fbm((((vec2<f32>(cos(_v3), sin(_v3)) * _v0) * 3.0) + vec2<f32>((U.time * 0.12), (-(U.time * 0.09))))); return vec4<f32>(((palette(((((_v4 * 0.7) + (((sin(((_v0 * 9.0) - (U.time * 0.8))) * 0.5) + 0.5) * 0.15)) + (_v0 * 0.3)) - (U.time * 0.03))) * ((_v4 * 0.9) + 0.35)) * (1.0 - smoothstep(0.55, 1.25, _v0))), 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 segments;} U;float hash(vec2 p);float noise(vec2 p);float fbm(vec2 p);vec3 palette(float t);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));}
vec3 palette(float t) { return (vec3(0.5) + (cos(((t + vec3(0.0, 0.33, 0.67)) * 6.283)) * 0.5));}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)); float _v0 = length(_cse0); float _v1 = atan(_cse0.y, _cse0.x); float _v2 = (6.2831853 / U.segments); float _v3 = abs((mod(_v1, _v2) - (_v2 * 0.5))); float _v4 = fbm((((vec2(cos(_v3), sin(_v3)) * _v0) * 3.0) + vec2((U.time * 0.12), (-(U.time * 0.09))))); return vec4(((palette(((((_v4 * 0.7) + (((sin(((_v0 * 9.0) - (U.time * 0.8))) * 0.5) + 0.5) * 0.15)) + (_v0 * 0.3)) - (U.time * 0.03))) * ((_v4 * 0.9) + 0.35)) * (1.0 - smoothstep(0.55, 1.25, _v0))), 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": "segments", "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 kaleidoscope
— prints the WGSL, GLSL, reflection. Source:
GitHub.
New to the IR? See Concepts; the full authoring + emit surface is in the API reference.