Examples
generic starfield.ts
Starfield
A textureless night sky — three hash-grid layers decide per cell whether a star exists, where it sits, and how it twinkles; nearer layers drift faster for parallax. Density 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 — starfield (hash grid, parallax layers) ═══//// The classic star field without a single texture: three grid layers, each// cell hashing whether it holds a star, where inside the cell it sits, how it// twinkles — nearer layers drift faster for parallax. A faint diagonal band// stands in for the Milky Way. Showcases procedural hashing as a data source// (the same lattice-hash the noise examples build on, used directly).// WGSL (WebGPU) + GLSL ES 3.00 (WebGL2).
import { fn, module, u32, f32, toF32, vec2, vec3, vec4, sin, floor, fract, dot, mix, exp, step, distance, smoothstep, Loop, Var, Let, f32T, vec2fT,} from '../src/index.ts'import { VsOut, vs, fullscreenUniforms, screenCoords } from './_fullscreen.ts'import type { ShaderExample } from './_shared.ts'const U = fullscreenUniforms({ density: 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)),)
const fs = fn( 'fs', { vo: VsOut }, ({ vo }) => { const t = U.field.time const res = U.field.resolution const p = screenCoords(vo.uv, res) const col = Var(vec3(0, 0, 0)) // three parallax layers — nearer (coarser) layers drift faster Loop( u32(0), (i) => i.lt(u32(3)), (i) => { const fi = toF32(i) const scale = fi.mul(14).add(18) const drift = fi.mul(0.014).add(0.01) const q = vec2(p.x.add(t.mul(drift)), p.y) .mul(scale) .add(fi.mul(37.7)) const cell = Let(floor(q)) const f = fract(q) const h = Let(hash({ p: cell })) // does this cell hold a star? density raises the hash gate const gate = step(f32(0.92).sub(U.field.density.mul(0.25)), h) // star position inside the cell (kept off the cell edges) const sp = vec2( hash({ p: cell.add(vec2(12.3, 45.6)) }), hash({ p: cell.add(vec2(78.9, 1.2)) }), ) .mul(0.7) .add(0.15) const d = distance(f, sp) const rad = f32(0.06).sub(fi.mul(0.012)) // far layers are smaller const core = Let(f32(1).sub(smoothstep(0, rad, d))) const twinkle = sin(t.mul(h.mul(4).add(2)).add(h.mul(40))) .mul(0.4) .add(0.6) const b = core .mul(core) .mul(twinkle) .mul(gate) .mul(f32(1).sub(fi.mul(0.25))) // colour temperature from the hash: blue-white ↔ warm const tint = mix(vec3(0.75, 0.85, 1.0), vec3(1.0, 0.9, 0.75), h) col.assign(col.add(tint.mul(b))) }, ) // a faint diagonal Milky-Way band const s = Let(p.y.add(p.x.mul(0.35))) const band = exp(s.mul(s).mul(6).neg()) return vec4(col.add(vec3(0.09, 0.11, 0.16).mul(band)), 1) }, { stage: 'fragment', retAttr: '@location(0)' },)
const starfieldModule = module({ structs: [U.struct, VsOut.decl], bindings: [U.binding], funcs: [vs, fs],})
export const starfield: ShaderExample = { id: 'starfield', title: 'Starfield', blurb: 'A textureless night sky — three hash-grid layers decide per cell whether a star exists, where it sits, and how it twinkles; nearer layers drift faster for parallax. Density is live.', category: 'generic', file: 'starfield.ts', module: starfieldModule, renderable: true, controls: { time: { kind: 'time' }, resolution: { kind: 'resolution' }, density: { kind: 'slider', label: 'Density', min: 0, max: 1, step: 0.05, value: 0.5 }, },}struct Uniforms { time: f32, resolution: vec2<f32>, density: 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));}
@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 _cse2 = vec2<f32>((((vo.uv.x * 2.0) - 1.0) * (U.resolution.x / U.resolution.y)), ((vo.uv.y * 2.0) - 1.0)); let _licm0 = U.time; let _licm1 = vec2<f32>(12.3, 45.6); let _licm2 = vec2<f32>(78.9, 1.2); let _licm3 = vec3<f32>(0.75, 0.85, 1.0); let _licm4 = vec3<f32>(1.0, 0.9, 0.75); let _licm5 = (0.92 - (U.density * 0.25)); let _cse0 = _cse2.x; let _cse1 = _cse2.y; var _v0: vec3<f32> = vec3<f32>(0.0, 0.0, 0.0); for (var _v1: u32 = 0u; (_v1 < 3u); _v1 = (_v1 + 1u)) { let _lc0 = f32(_v1); let _v2 = floor(((vec2<f32>((_cse0 + (_licm0 * ((_lc0 * 0.014) + 0.01))), _cse1) * ((_lc0 * 14.0) + 18.0)) + (_lc0 * 37.7))); let _v3 = hash(_v2); let _lc1 = f32(_v1); let _v4 = (1.0 - smoothstep(0.0, (0.06 - (_lc1 * 0.012)), distance(fract(((vec2<f32>((_cse0 + (_licm0 * ((_lc1 * 0.014) + 0.01))), _cse1) * ((_lc1 * 14.0) + 18.0)) + (_lc1 * 37.7))), ((vec2<f32>(hash((_v2 + _licm1)), hash((_v2 + _licm2))) * 0.7) + 0.15)))); _v0 = (_v0 + (mix(_licm3, _licm4, _v3) * ((((_v4 * _v4) * ((sin(((_licm0 * ((_v3 * 4.0) + 2.0)) + (_v3 * 40.0))) * 0.4) + 0.6)) * step(_licm5, _v3)) * (1.0 - (f32(_v1) * 0.25))))); } let _v5 = (_cse1 + (_cse0 * 0.35)); return vec4<f32>((_v0 + (vec3<f32>(0.09, 0.11, 0.16) * exp((-((_v5 * _v5) * 6.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 density;} U;float hash(vec2 p);float hash(vec2 p) { return fract((sin(dot(p, vec2(127.1, 311.7))) * 43758.5453));}in vec2 uv;layout(location = 0) out vec4 _ret;
vec4 fs_impl(VsOut vo) { vec2 _cse2 = vec2((((vo.uv.x * 2.0) - 1.0) * (U.resolution.x / U.resolution.y)), ((vo.uv.y * 2.0) - 1.0)); float _licm0 = U.time; vec2 _licm1 = vec2(12.3, 45.6); vec2 _licm2 = vec2(78.9, 1.2); vec3 _licm3 = vec3(0.75, 0.85, 1.0); vec3 _licm4 = vec3(1.0, 0.9, 0.75); float _licm5 = (0.92 - (U.density * 0.25)); float _cse0 = _cse2.x; float _cse1 = _cse2.y; vec3 _v0 = vec3(0.0, 0.0, 0.0); for (uint _v1 = 0u; (_v1 < 3u); _v1 = (_v1 + 1u)) { float _lc0 = float(_v1); vec2 _v2 = floor(((vec2((_cse0 + (_licm0 * ((_lc0 * 0.014) + 0.01))), _cse1) * ((_lc0 * 14.0) + 18.0)) + (_lc0 * 37.7))); float _v3 = hash(_v2); float _lc1 = float(_v1); float _v4 = (1.0 - smoothstep(0.0, (0.06 - (_lc1 * 0.012)), distance(fract(((vec2((_cse0 + (_licm0 * ((_lc1 * 0.014) + 0.01))), _cse1) * ((_lc1 * 14.0) + 18.0)) + (_lc1 * 37.7))), ((vec2(hash((_v2 + _licm1)), hash((_v2 + _licm2))) * 0.7) + 0.15)))); _v0 = (_v0 + (mix(_licm3, _licm4, _v3) * ((((_v4 * _v4) * ((sin(((_licm0 * ((_v3 * 4.0) + 2.0)) + (_v3 * 40.0))) * 0.4) + 0.6)) * step(_licm5, _v3)) * (1.0 - (float(_v1) * 0.25))))); } float _v5 = (_cse1 + (_cse0 * 0.35)); return vec4((_v0 + (vec3(0.09, 0.11, 0.16) * exp((-((_v5 * _v5) * 6.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": "density", "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 starfield
— prints the WGSL, GLSL, reflection. Source:
GitHub.
New to the IR? See Concepts; the full authoring + emit surface is in the API reference.