Examples
generic tunnel.ts
Tunnel
The demoscene tunnel — the screen remapped to polar coordinates (angle around, 1/radius into the depth) so a checker pattern flies past forever. atan2 + length + fog, with a live twist control.
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 — the classic tunnel (polar remap) ═══//// The demoscene / ShaderToy staple: remap the screen into polar coordinates// (angle around the bore, 1/radius into the depth) so a flat checker pattern// reads as an endless tunnel flying past. Showcases `atan2` (the divergent// WGSL/GLSL spelling the intrinsic registry unifies), `length`, and// screen-space fog. One DSL source → WGSL (WebGPU) + GLSL ES 3.00 (WebGL2).
import { fn, module, f32, vec3, vec4, sin, atan2, length, max, mix, clamp, smoothstep, Let, f32T,} from '../src/index.ts'import { VsOut, vs, fullscreenUniforms, screenCoords } from './_fullscreen.ts'import type { ShaderExample } from './_shared.ts'const U = fullscreenUniforms({ twist: f32T })
const fs = fn( 'fs', { vo: VsOut }, ({ vo }) => { const t = U.field.time const res = U.field.resolution // centred, aspect-corrected plane const p = screenCoords(vo.uv, res) const r = Let(length(p)) const a = atan2(p.y, p.x) // tunnel-surface coordinates: 1/r is the distance into the bore, the // angle wraps around it. Adding time to the depth flies the camera forward; // the twist uniform shears the angle by depth so the bore corkscrews. const depth = Let(f32(0.3).div(max(r, 0.001)).add(t.mul(1.4))) const ang = a.div(3.14159265).add(depth.mul(U.field.twist).mul(0.08)) // checkerboard walls: two perpendicular square-ish waves const cw = sin(ang.mul(12.566)).mul(sin(depth.mul(9.4248))) const shade = smoothstep(-0.6, 0.6, cw).mul(0.55).add(0.35) // warm→cool tint cycling with depth, then fog toward the far centre const tint = mix( vec3(1.0, 0.62, 0.28), vec3(0.42, 0.3, 0.55), sin(depth.mul(0.9)).mul(0.5).add(0.5), ) const fog = smoothstep(0.0, 0.55, r) const vig = clamp(f32(1.15).sub(r.mul(0.35)), 0, 1) return vec4(tint.mul(shade).mul(fog).mul(vig), 1) }, { stage: 'fragment', retAttr: '@location(0)' },)
const tunnelModule = module({ structs: [U.struct, VsOut.decl], bindings: [U.binding], funcs: [vs, fs],})
export const tunnel: ShaderExample = { id: 'tunnel', title: 'Tunnel', blurb: 'The demoscene tunnel — the screen remapped to polar coordinates (angle around, 1/radius into the depth) so a checker pattern flies past forever. atan2 + length + fog, with a live twist control.', category: 'generic', file: 'tunnel.ts', module: tunnelModule, renderable: true, controls: { time: { kind: 'time' }, resolution: { kind: 'resolution' }, twist: { kind: 'slider', label: 'Twist', min: 0, max: 3, step: 0.1, value: 1 }, },}struct Uniforms { time: f32, resolution: vec2<f32>, twist: f32,}
struct VsOut { @builtin(position) pos: vec4<f32>, @location(0) uv: vec2<f32>,}
@group(0) @binding(0) var<uniform> U: Uniforms;
@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 = ((0.3 / max(_v0, 0.001)) + (U.time * 1.4)); return vec4<f32>((((mix(vec3<f32>(1.0, 0.62, 0.28), vec3<f32>(0.42, 0.3, 0.55), ((sin((_v1 * 0.9)) * 0.5) + 0.5)) * ((smoothstep(-0.6, 0.6, (sin((((atan2(_cse0.y, _cse0.x) / 3.14159265) + ((_v1 * U.twist) * 0.08)) * 12.566)) * sin((_v1 * 9.4248)))) * 0.55) + 0.35)) * smoothstep(0.0, 0.55, _v0)) * clamp((1.15 - (_v0 * 0.35)), 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 twist;} U;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 = ((0.3 / max(_v0, 0.001)) + (U.time * 1.4)); return vec4((((mix(vec3(1.0, 0.62, 0.28), vec3(0.42, 0.3, 0.55), ((sin((_v1 * 0.9)) * 0.5) + 0.5)) * ((smoothstep(-0.6, 0.6, (sin((((atan(_cse0.y, _cse0.x) / 3.14159265) + ((_v1 * U.twist) * 0.08)) * 12.566)) * sin((_v1 * 9.4248)))) * 0.55) + 0.35)) * smoothstep(0.0, 0.55, _v0)) * clamp((1.15 - (_v0 * 0.35)), 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": "twist", "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 tunnel
— prints the WGSL, GLSL, reflection. Source:
GitHub.
New to the IR? See Concepts; the full authoring + emit surface is in the API reference.