Examples
generic heart.ts
Beating heart
The sextic heart curve (x²+y²−1)³ = x²y³, sign-filled and fwidth-anti-aliased, thumping to a sharpened-sine heartbeat with a beat-synced glow. Beat rate 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 — beating heart (implicit curve) ═══//// The sextic heart curve (x² + y² − 1)³ − x²y³ = 0, filled where the implicit// function goes negative, anti-aliased with `fwidth` on the field itself, and// scaled by a sharpened-sine heartbeat. A different shading recipe from the// SDF examples: implicit-function sign fill + field-driven inner gradient +// exponential outer glow synced to the beat. WGSL (WebGPU) + GLSL ES 3.00.
import { fn, module, f32, vec3, vec4, sin, abs, pow, max, mix, exp, clamp, smoothstep, fwidth, Let, f32T,} from '../src/index.ts'import { VsOut, vs, fullscreenUniforms, screenCoords } from './_fullscreen.ts'import type { ShaderExample } from './_shared.ts'
const U = fullscreenUniforms({ beat: f32T })
const fs = fn( 'fs', { vo: VsOut }, ({ vo }) => { const t = U.field.time const res = U.field.resolution const p = screenCoords(vo.uv, res) // heartbeat: |sin|⁸ sharpens the sine into a thump per half-period const beat = Let(pow(abs(sin(t.mul(3.14159).mul(U.field.beat))), 8).mul(0.12)) const s = Let(f32(0.72).add(beat)) const qx = Let(p.x.mul(1.3).div(s)) const qy = Let(p.y.add(0.08).mul(1.3).div(s)) // the sextic heart: (x² + y² − 1)³ − x²·y³ const xx = Let(qx.mul(qx)) const k = Let(xx.add(qy.mul(qy)).sub(1)) const f = Let(k.mul(k).mul(k).sub(xx.mul(qy).mul(qy).mul(qy))) // fill where f < 0, anti-aliased on the field's own screen gradient const aa = Let(fwidth(f).mul(1.6)) const fill = f32(1).sub(smoothstep(aa.neg(), aa, f)) // inside: brighter toward the centre (f is most negative there) const heart = mix(vec3(0.55, 0.03, 0.1), vec3(0.95, 0.15, 0.25), clamp(f.neg(), 0, 1)) // outside: a soft glow that swells with the beat const glow = exp(max(f, 0).mul(2.2).neg()).mul(beat.mul(3).add(0.35)) const bg = vec3(0.05, 0.04, 0.07).mul(f32(1).sub(vo.uv.y.mul(0.35))) const col = mix(bg.add(vec3(0.7, 0.08, 0.16).mul(glow)), heart, fill) return vec4(col, 1) }, { stage: 'fragment', retAttr: '@location(0)' },)
const heartModule = module({ structs: [U.struct, VsOut.decl], bindings: [U.binding], funcs: [vs, fs],})
export const heart: ShaderExample = { id: 'heart', title: 'Beating heart', blurb: 'The sextic heart curve (x²+y²−1)³ = x²y³, sign-filled and fwidth-anti-aliased, thumping to a sharpened-sine heartbeat with a beat-synced glow. Beat rate is live.', category: 'generic', file: 'heart.ts', module: heartModule, renderable: true, controls: { time: { kind: 'time' }, resolution: { kind: 'resolution' }, beat: { kind: 'slider', label: 'Beat rate', min: 0.2, max: 2, step: 0.1, value: 1 }, },}struct Uniforms { time: f32, resolution: vec2<f32>, beat: 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 = (pow(abs(sin(((U.time * 3.14159) * U.beat))), 8.0) * 0.12); let _v1 = (0.72 + _v0); let _v2 = ((_cse0.x * 1.3) / _v1); let _v3 = (((_cse0.y + 0.08) * 1.3) / _v1); let _v4 = (_v2 * _v2); let _v5 = ((_v4 + (_v3 * _v3)) - 1.0); let _v6 = (((_v5 * _v5) * _v5) - (((_v4 * _v3) * _v3) * _v3)); let _v7 = (fwidth(_v6) * 1.6); return vec4<f32>(mix(((vec3<f32>(0.05, 0.04, 0.07) * (1.0 - (vo.uv.y * 0.35))) + (vec3<f32>(0.7, 0.08, 0.16) * (exp((-(max(_v6, 0.0) * 2.2))) * ((_v0 * 3.0) + 0.35)))), mix(vec3<f32>(0.55, 0.03, 0.1), vec3<f32>(0.95, 0.15, 0.25), clamp((-_v6), 0.0, 1.0)), (1.0 - smoothstep((-_v7), _v7, _v6))), 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 beat;} 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 = (pow(abs(sin(((U.time * 3.14159) * U.beat))), 8.0) * 0.12); float _v1 = (0.72 + _v0); float _v2 = ((_cse0.x * 1.3) / _v1); float _v3 = (((_cse0.y + 0.08) * 1.3) / _v1); float _v4 = (_v2 * _v2); float _v5 = ((_v4 + (_v3 * _v3)) - 1.0); float _v6 = (((_v5 * _v5) * _v5) - (((_v4 * _v3) * _v3) * _v3)); float _v7 = (fwidth(_v6) * 1.6); return vec4(mix(((vec3(0.05, 0.04, 0.07) * (1.0 - (vo.uv.y * 0.35))) + (vec3(0.7, 0.08, 0.16) * (exp((-(max(_v6, 0.0) * 2.2))) * ((_v0 * 3.0) + 0.35)))), mix(vec3(0.55, 0.03, 0.1), vec3(0.95, 0.15, 0.25), clamp((-_v6), 0.0, 1.0)), (1.0 - smoothstep((-_v7), _v7, _v6))), 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": "beat", "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 heart
— prints the WGSL, GLSL, reflection. Source:
GitHub.
New to the IR? See Concepts; the full authoring + emit surface is in the API reference.