Examples
generic gradient-pass.ts
Gradient pass
A two-colour vertical gradient with a biasable blend — the minimal render pass. `top`/`bottom`/`mix_bias` are packed into one std140 uniform block recovered from reflect().
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 — fullscreen gradient pass ═══//// A self-contained render pass: an oversized fullscreen triangle (no vertex buffer)// feeds a fragment stage that mixes two colours by the screen-space UV, modulated by a// uniform. Shows the std140 uniform block + the `If/elif` control-flow combinator. The// /shader-dsl site page packs `top`/`bottom`/`mix_bias` into the UBO from reflect().
import { fn, module, vec2, vec4, f32, mix, f32T, u32T, vec2fT, vec4fT, If, ioStruct, builtin, location, uniformStruct,} from '../src/index.ts'import type { ShaderExample } from './_shared.ts'
// Uniform block — std140-laid-out by reflect(). `top`/`bottom` are the two gradient// endpoints; `mix_bias` shifts the blend up or down.const U = uniformStruct( 'Uniforms', { group: 0, binding: 0, as: 'u' }, { top: vec4fT, bottom: vec4fT, mix_bias: f32T, },)
// Vertex → fragment interface struct.const VsOut = ioStruct('VsOut', { pos: builtin('position', vec4fT), uv: location(0, vec2fT),})
// Oversized fullscreen triangle (3 verts, NDC −1..3) — covers the screen from a single// non-indexed draw with no vertex buffer.const vsFull = fn( 'vs_full', { idx: builtin('vertex_index', u32T) }, (p) => { const pos = vec2(-1, -1) If(p.idx.eq(1), () => { pos.assign(vec2(3, -1)) }).elif(p.idx.eq(2), () => { pos.assign(vec2(-1, 3)) }) return VsOut.construct({ pos: vec4(pos, 0, 1), uv: vec2(pos.x.add(1).mul(0.5), pos.y.add(1).mul(0.5)), }) }, { stage: 'vertex' },)
// Fragment — vertical gradient between the two uniform colours, biased. `vo` (not `in`,// a GLSL reserved word) is the fragment input.const fsGradient = fn( 'fs_gradient', { vo: VsOut }, (p) => { const t = p.vo.uv.y.add(U.field.mix_bias) const rgb = mix(U.field.bottom.rgb, U.field.top.rgb, t) return vec4(rgb, f32(1)) }, { stage: 'fragment', retAttr: '@location(0)' },)
const gradientModule = module({ structs: [U.struct, VsOut.decl], bindings: [U.binding], funcs: [vsFull, fsGradient],})
export const gradient: ShaderExample = { id: 'gradient', title: 'Gradient pass', blurb: 'A two-colour vertical gradient with a biasable blend — the minimal render pass. `top`/`bottom`/`mix_bias` are packed into one std140 uniform block recovered from reflect().', category: 'generic', file: 'gradient-pass.ts', module: gradientModule, renderable: true, controls: { top: { kind: 'const', value: [0.16, 0.5, 0.95, 1] }, // sky blue bottom: { kind: 'const', value: [0.02, 0.03, 0.09, 1] }, // near-black mix_bias: { kind: 'slider', label: 'Mix bias', min: -0.5, max: 0.5, step: 0.01, value: 0 }, },}struct Uniforms { top: vec4<f32>, bottom: vec4<f32>, mix_bias: f32,}
struct VsOut { @builtin(position) pos: vec4<f32>, @location(0) uv: vec2<f32>,}
@group(0) @binding(0) var<uniform> u: Uniforms;
@vertexfn vs_full(@builtin(vertex_index) idx: u32) -> VsOut { var _av0: vec2<f32> = vec2<f32>(-1.0, -1.0); if ((idx == 1u)) { _av0 = vec2<f32>(3.0, -1.0); } else if ((idx == 2u)) { _av0 = vec2<f32>(-1.0, 3.0); } return VsOut(vec4<f32>(_av0, 0.0, 1.0), vec2<f32>(((_av0.x + 1.0) * 0.5), ((_av0.y + 1.0) * 0.5)));}
@fragmentfn fs_gradient(vo: VsOut) -> @location(0) vec4<f32> { return vec4<f32>(mix(u.bottom.rgb, u.top.rgb, (vo.uv.y + u.mix_bias)), 1.0);}#version 300 esprecision highp float;precision highp int;
struct VsOut { vec4 pos; vec2 uv;};out vec2 uv;
VsOut vs_full_impl(uint idx) { vec2 _av0 = vec2(-1.0, -1.0); if ((idx == 1u)) { _av0 = vec2(3.0, -1.0); } else if ((idx == 2u)) { _av0 = vec2(-1.0, 3.0); } return VsOut(vec4(_av0, 0.0, 1.0), vec2(((_av0.x + 1.0) * 0.5), ((_av0.y + 1.0) * 0.5)));}
void main() { VsOut _out = vs_full_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 { vec4 top; vec4 bottom; float mix_bias;} u;in vec2 uv;layout(location = 0) out vec4 _ret;
vec4 fs_gradient_impl(VsOut vo) { return vec4(mix(u.bottom.rgb, u.top.rgb, (vo.uv.y + u.mix_bias)), 1.0);}
void main() { VsOut vo; vo.pos = gl_FragCoord; vo.uv = uv; _ret = fs_gradient_impl(vo);}{ "bindGroups": [ { "group": 0, "entries": [ { "group": 0, "binding": 0, "name": "u", "space": "uniform", "resourceKind": "uniform-buffer", "structName": "Uniforms" } ] } ], "uniforms": [ { "name": "Uniforms", "size": 48, "align": 16, "fields": [ { "name": "top", "type": "vec4<f32>", "offset": 0, "align": 16, "size": 16 }, { "name": "bottom", "type": "vec4<f32>", "offset": 16, "align": 16, "size": 16 }, { "name": "mix_bias", "type": "f32", "offset": 32, "align": 4, "size": 4 } ] } ], "storage": [], "entries": [ { "name": "vs_full", "stage": "vertex", "inputs": [ "u32" ], "output": "struct:VsOut" }, { "name": "fs_gradient", "stage": "fragment", "inputs": [ "struct:VsOut" ], "output": "vec4<f32>" } ], "overrides": []}
Run it locally:
npx tsx examples/print.ts gradient
— prints the WGSL, GLSL, reflection. Source:
GitHub.
New to the IR? See Concepts; the full authoring + emit surface is in the API reference.