Examples
cartographic color-ramp.ts
Colour ramp
The data-driven choropleth ramp: a reusable ramp() function maps a value field through a 5-stop palette, with anti-aliased contour isolines. Set how many bands to draw.
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 — choropleth colour ramp ═══//// A cartographic shader: the data-driven colour ramp a thematic map uses to paint a value// field. A reusable `ramp()` DSL function maps a normalised value [0,1] through a 5-stop// sequential palette; the fragment samples an animated value field, colours it, and overlays// anti-aliased contour isolines whose count is a live uniform.
import { fn, module, vec3, vec4, sin, fract, smoothstep, min, clamp, fwidth, mix, f32, f32T,} from '../src/index.ts'import { VsOut, vs, fullscreenUniforms } from './_fullscreen.ts'import type { ShaderExample } from './_shared.ts'const U = fullscreenUniforms({ bands: f32T })
// 5-stop sequential ramp (light yellow → deep red), a classic choropleth legend.const ramp = fn('ramp', { x: f32T }, ({ x }) => { const c0 = vec3(0.99, 0.95, 0.74) const c1 = vec3(0.99, 0.8, 0.45) const c2 = vec3(0.96, 0.5, 0.24) const c3 = vec3(0.84, 0.19, 0.15) const c4 = vec3(0.5, 0.0, 0.05) const a = mix(c0, c1, smoothstep(0, 0.25, x)) const b = mix(a, c2, smoothstep(0.25, 0.5, x)) const d = mix(b, c3, smoothstep(0.5, 0.75, x)) return mix(d, c4, smoothstep(0.75, 1, x))})
const fs = fn( 'fs', { vo: VsOut }, ({ vo }) => { const uv = vo.uv const t = U.field.time const bands = U.field.bands
// Animated data field in [0,1]. const val = clamp( sin(uv.x.mul(6.2832).add(t.mul(0.5))) .mul(0.3) .add(0.5) .add(sin(uv.y.mul(6.2832).sub(t.mul(0.35))).mul(0.2)), 0, 1, )
const col = ramp({ x: val })
// Contour isolines at evenly spaced value levels, screen-constant width via fwidth. const e = fract(val.mul(bands)) const dd = min(e, f32(1).sub(e)) const lineMask = f32(1).sub(smoothstep(0, fwidth(val.mul(bands)).mul(1.2), dd)) const enabled = smoothstep(0.5, 1.5, bands) // disables the overlay when bands ≈ 0 const contour = clamp(lineMask.mul(enabled), 0, 1)
return vec4(mix(col, col.mul(0.2), contour), 1) }, { stage: 'fragment', retAttr: '@location(0)' },)
// `ramp` is called via its handle in `fs`, so module() collects it transitively — funcs lists only the entry points.const colorRampModule = module({ structs: [U.struct, VsOut.decl], bindings: [U.binding], funcs: [vs, fs],})
export const colorRamp: ShaderExample = { id: 'color-ramp', title: 'Colour ramp', blurb: 'The data-driven choropleth ramp: a reusable ramp() function maps a value field through a 5-stop palette, with anti-aliased contour isolines. Set how many bands to draw.', category: 'cartographic', file: 'color-ramp.ts', module: colorRampModule, renderable: true, controls: { time: { kind: 'time' }, resolution: { kind: 'resolution' }, bands: { kind: 'slider', label: 'Contour bands', min: 0, max: 12, step: 1, value: 6 }, },}struct Uniforms { time: f32, resolution: vec2<f32>, bands: f32,}
struct VsOut { @builtin(position) pos: vec4<f32>, @location(0) uv: vec2<f32>,}
@group(0) @binding(0) var<uniform> U: Uniforms;
fn ramp(x: f32) -> vec3<f32> { return mix(mix(mix(mix(vec3<f32>(0.99, 0.95, 0.74), vec3<f32>(0.99, 0.8, 0.45), smoothstep(0.0, 0.25, x)), vec3<f32>(0.96, 0.5, 0.24), smoothstep(0.25, 0.5, x)), vec3<f32>(0.84, 0.19, 0.15), smoothstep(0.5, 0.75, x)), vec3<f32>(0.5, 0.0, 0.05), smoothstep(0.75, 1.0, x));}
@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 _cse3 = clamp((((sin(((vo.uv.x * 6.2832) + (U.time * 0.5))) * 0.3) + 0.5) + (sin(((vo.uv.y * 6.2832) - (U.time * 0.35))) * 0.2)), 0.0, 1.0); let _cse2 = (_cse3 * U.bands); let _cse0 = ramp(_cse3); let _cse1 = fract(_cse2); return vec4<f32>(mix(_cse0, (_cse0 * 0.2), clamp(((1.0 - smoothstep(0.0, (fwidth(_cse2) * 1.2), min(_cse1, (1.0 - _cse1)))) * smoothstep(0.5, 1.5, U.bands)), 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 bands;} U;vec3 ramp(float x);vec3 ramp(float x) { return mix(mix(mix(mix(vec3(0.99, 0.95, 0.74), vec3(0.99, 0.8, 0.45), smoothstep(0.0, 0.25, x)), vec3(0.96, 0.5, 0.24), smoothstep(0.25, 0.5, x)), vec3(0.84, 0.19, 0.15), smoothstep(0.5, 0.75, x)), vec3(0.5, 0.0, 0.05), smoothstep(0.75, 1.0, x));}in vec2 uv;layout(location = 0) out vec4 _ret;
vec4 fs_impl(VsOut vo) { float _cse3 = clamp((((sin(((vo.uv.x * 6.2832) + (U.time * 0.5))) * 0.3) + 0.5) + (sin(((vo.uv.y * 6.2832) - (U.time * 0.35))) * 0.2)), 0.0, 1.0); float _cse2 = (_cse3 * U.bands); vec3 _cse0 = ramp(_cse3); float _cse1 = fract(_cse2); return vec4(mix(_cse0, (_cse0 * 0.2), clamp(((1.0 - smoothstep(0.0, (fwidth(_cse2) * 1.2), min(_cse1, (1.0 - _cse1)))) * smoothstep(0.5, 1.5, U.bands)), 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": "bands", "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 color-ramp
— prints the WGSL, GLSL, reflection. Source:
GitHub.
New to the IR? See Concepts; the full authoring + emit surface is in the API reference.