Skip to content

Instantly share code, notes, and snippets.

@munrocket
Last active November 2, 2025 16:46
Show Gist options
  • Select an option

  • Save munrocket/30e645d584b5300ee69295e54674b3e4 to your computer and use it in GitHub Desktop.

Select an option

Save munrocket/30e645d584b5300ee69295e54674b3e4 to your computer and use it in GitHub Desktop.
WGSL 2D SDF Primitives

WGSL 2D SDF Primitives

Revision: 06.08.2023, https://compute.toys/view/398

Circle - exact

fn sdCircle(p: vec2f, r: f32) -> f32 {
  return length(p) - r;
}

Rounded Box - exact

fn sdRoundedBox(p: vec2f, b: vec2f, r: vec4f) -> f32 {
  var x = r.x;
  var y = r.y;
  x = select(r.z, r.x, p.x > 0.);
  y = select(r.w, r.y, p.x > 0.);
  x  = select(y, x, p.y > 0.);
  let q = abs(p) - b + x;
  return min(max(q.x, q.y), 0.) + length(max(q, vec2f(0.))) - x;
}

Box - exact

fn sdBox(p: vec2f, b: vec2f) -> f32 {
  let d = abs(p) - b;
  return length(max(d, vec2f(0.))) + min(max(d.x, d.y), 0.);
}

Oriented Box - exact

fn sdOrientedBox(p: vec2f, a: vec2f, b: vec2f, th: f32) -> f32 {
  let l = length(b - a);
  let d = (b - a) / l;
  var q = p - (a + b) * 0.5;
  q = q * mat2x2<f32>(vec2f(d.x, d.y), vec2f(-d.y, d.x));
  q = abs(q) - vec2f(l, th) * 0.5;
  return length(max(q, vec2f(0.))) + min(max(q.x, q.y), 0.);
}

Segment - exact

fn sdSegment(p: vec2f, a: vec2f, b: vec2f) -> f32 {
  let pa = p - a;
  let ba = b - a;
  let h = clamp(dot(pa, ba) / dot(ba, ba), 0., 1.);
  return length(pa - ba * h);
}

Rhombus - exact

fn sdRhombus(p: vec2f, b: vec2f) -> f32 {
  let q = abs(p);
  let qb = dot(q, vec2f(b.x, -b.y));
  let bb = dot(b, vec2f(b.x, -b.y));
  let h = clamp((-2. * qb + bb) / dot(b, b), -1., 1.);
  let d = length(q - 0.5 * b * vec2f(1. - h, 1. + h));
  return d * sign(q.x * b.y + q.y * b.x - b.x * b.y);
}

Isosceles Trapezoid - exact

fn sdTrapezoid(p: vec2f, r1: f32, r2: f32, he: f32) -> f32 {
  let k1 = vec2f(r2, he);
  let k2 = vec2f(r2 - r1, 2. * he);
  let q = vec2f(abs(p.x), p.y);
  let ca = vec2f(q.x - min(q.x, select(r2, r1, q.y < 0.0)), abs(q.y) - he);
  let cb = q - k1 + k2 * clamp(dot(k1 - q, k2) / dot(k2, k2), 0., 1.);
  let s = select(1., -1., cb.x < 0.0 && ca.y < 0.0);
  return s * sqrt(min(dot(ca, ca), dot(cb, cb)));
}

Parallelogram - exact

fn sdParallelogram(p: vec2f, wi: f32, he: f32, sk: f32) -> f32 {
  let e = vec2f(sk, he);
  var q: vec2f = select(p, -p, p.y < 0.);
  // horizontal edge
  var w: vec2f = q - e;
  w.x = w.x - clamp(w.x, -wi, wi);
  var d: vec2f = vec2f(dot(w, w), -w.y);
  // vertical edge
  let s = q.x * e.y - q.y * e.x;
  q = select(q, -q, s < 0.);
  var v: vec2f = q - vec2f(wi, 0.);
  v = v - e * clamp(dot(v, e) / dot(e, e), -1., 1.);
  d = min(d, vec2f(dot(v, v), wi * he - abs(s)));
  return sqrt(d.x) * sign(-d.y);
}

Equilateral Triangle - exact

fn sdEquilateralTriangle(p: vec2f) -> f32 {
  let k = sqrt(3.);
  var q: vec2f = vec2f(abs(p.x) - 1.0, p.y + 1. / k);
  if (q.x + k * q.y > 0.) { q = vec2f(q.x - k * q.y, -k * q.x - q.y) / 2.; }
  q.x = q.x - clamp(q.x, -2., 0.);
  return -length(q) * sign(q.y);
}

Isosceles Triangle - exact

fn sdTriangleIsosceles(p: vec2f, c: vec2f) -> f32 {
  let q = vec2f(abs(p.x), p.y);
  let a = q - c * clamp(dot(q, c) / dot(c, c), 0., 1.);
  let b = q - c * vec2f(clamp(q.x / c.x, 0., 1.), 1.);
  let s = -sign(c.y);
  let d = min(vec2f(dot(a, a), s * (q.x * c.y - q.y * c.x)), vec2f(dot(b, b), s * (q.y - c.y)));
  return -sqrt(d.x) * sign(d.y);
}

Triangle - exact

fn sdTriangle(p: vec2f, p0: vec2f, p1: vec2f, p2: vec2f) -> f32 {
  let e0 = p1 - p0; let e1 = p2 - p1; let e2 = p0 - p2;
  let v0 = p - p0; let v1 = p - p1; let v2 = p - p2;
  let pq0 = v0 - e0 * clamp(dot(v0, e0) / dot(e0, e0), 0., 1.);
  let pq1 = v1 - e1 * clamp(dot(v1, e1) / dot(e1, e1), 0., 1.);
  let pq2 = v2 - e2 * clamp(dot(v2, e2) / dot(e2, e2), 0., 1.);
  let s = sign(e0.x * e2.y - e0.y * e2.x);
  let d = min(min(vec2f(dot(pq0, pq0), s * (v0.x * e0.y - v0.y * e0.x)),
                  vec2f(dot(pq1, pq1), s * (v1.x * e1.y - v1.y * e1.x))),
                  vec2f(dot(pq2, pq2), s * (v2.x * e2.y - v2.y * e2.x)));
  return -sqrt(d.x) * sign(d.y);
}

Uneven Capsule - exact

fn sdUnevenCapsule(p: vec2f, r1: f32, r2: f32, h: f32) -> f32 {
  let q = vec2f(abs(p.x), p.y);
  let b = (r1 - r2) / h;
  let a = sqrt(1. - b * b);
  let k = dot(q, vec2f(-b, a));
  if (k < 0.) { return length(q) - r1; }
  if (k > a * h) { return length(q - vec2f(0., h)) - r2; }
  return dot(q, vec2f(a, b)) - r1;
}

Regular Pentagon - exact

fn sdPentagon(p: vec2f, r: f32) -> f32 {
  let k = vec3f(0.809016994, 0.587785252, 0.726542528);
  var q: vec2f = vec2f(abs(p.x), p.y);
  q = q - 2. * min(dot(vec2f(-k.x, k.y), q), 0.) * vec2f(-k.x, k.y);
  q = q - 2. * min(dot(vec2f(k.x, k.y), q), 0.) * vec2f(k.x, k.y);
  q = q - vec2f(clamp(q.x, -r * k.z, r * k.z), r);
  return length(q) * sign(q.y);
}

Regular Hexagon - exact

fn sdHexagon(p: vec2f, r: f32) -> f32 {
  let k = vec3f(-0.866025404, 0.5, 0.577350269);
  var q: vec2f = abs(p);
  q = q - 2. * min(dot(k.xy, q), 0.) * k.xy;
  q = q - vec2f(clamp(q.x, -k.z * r, k.z * r), r);
  return length(q) * sign(q.y);
}

Regular Octogon - exact

fn sdOctogon(p: vec2f, r: f32) -> f32 {
  let k = vec3f(-0.9238795325, 0.3826834323, 0.4142135623);
  var q: vec2f = abs(p);
  q = q - 2. * min(dot(vec2f(k.x, k.y), q), 0.) * vec2f(k.x, k.y);
  q = q - 2. * min(dot(vec2f(-k.x, k.y), q), 0.) * vec2f(-k.x, k.y);
  q = q - vec2f(clamp(q.x, -k.z * r, k.z * r), r);
  return length(q) * sign(q.y);
}

Hexagram - exact

fn sdHexagram(p: vec2f, r: f32) -> f32 {
  let k = vec4f(-0.5, 0.8660254038, 0.5773502692, 1.7320508076);
  var q: vec2f = abs(p);
  q = q - 2. * min(dot(k.xy, q), 0.) * k.xy;
  q = q - 2. * min(dot(k.yx, q), 0.) * k.yx;
  q = q - vec2f(clamp(q.x, r * k.z, r * k.w), r);
  return length(q) * sign(q.y);
}

Star 5 - exact

fn sdStar5(p: vec2f, r: f32, rf: f32) -> f32 {
  let k1 = vec2f(0.809016994375, -0.587785252292);
  let k2 = vec2f(-k1.x, k1.y);
  var q: vec2f = vec2f(abs(p.x), p.y);
  q = q - 2. * max(dot(k1, q), 0.) * k1;
  q = q - 2. * max(dot(k2, q), 0.) * k2;
  q.x = abs(q.x);
  q.y = q.y - r;
  let ba = rf * vec2f(-k1.y, k1.x) - vec2f(0., 1.);
  let h = clamp(dot(q, ba) / dot(ba, ba), 0., r);
  return length(q - ba * h) * sign(q.y * ba.x - q.x * ba.y);
}

Regular Star - exact

fn sdStar(p: vec2f, r: f32, n: u32, m: f32) ->f32 {
  let an = 3.141593 / f32(n);
  let en = 3.141593 / m;
  let acs = vec2f(cos(an), sin(an));
  let ecs = vec2f(cos(en), sin(en));
  let bn = (atan2(abs(p.x), p.y) % (2. * an)) - an;
  var q: vec2f = length(p) * vec2f(cos(bn), abs(sin(bn)));
  q = q - r * acs;
  q = q + ecs * clamp(-dot(q, ecs), 0., r * acs.y / ecs.y);
  return length(q) * sign(q.x);
}

Pie - exact

fn sdPie(p: vec2f, sc: vec2f, r: f32) -> f32 {
  let q = vec2f(abs(p.x), p.y);
  let l = length(q) - r;
  let m = length(q - sc * clamp(dot(q, sc), 0., r));
  return max(l, m * sign(sc.y * q.x - sc.x * q.y));
}

Arc - exact

fn sdArc(p: vec2f, sc1: vec2f, sc2: vec2f, r1: f32, r2: f32) -> f32 {
  var q: vec2f = p * mat2x2<f32>(vec2f(sc1.x, sc1.y), vec2f(-sc1.y, sc1.x));
  q.x = abs(q.x);
  let k = select(length(q), dot(q, sc2), sc2.y * q.x > sc2.x * q.y);
  return sqrt(dot(q, q) + r1 * r1 - 2. * r1 * k) - r2;
}

Horseshoe - exact

fn sdHorseshoe(p: vec2f, sc: vec2f, r: f32, l: f32, w: f32) -> f32 {
  var q: vec2f = vec2f(abs(p.x), p.y);
  let m = length(p);
  q = q * mat2x2<f32>(vec2f(-sc.y, sc.x), vec2f(sc.x, sc.y));
  q = vec2f(select(m * sign(-sc.y), q.x, q.y > 0.0 || q.x > 0.), select(m, q.y, q.x > 0.));
  q = vec2f(q.x, abs(q.y - r)) - vec2f(l, w);
  return length(max(q, vec2f(0.))) + min(0., max(q.x, q.y));
}

Vesica - exact

fn sdVesica(p: vec2f, r: f32, d: f32) -> f32 {
  let q = abs(p);
  let b = sqrt(r * r - d * d);
  let cond = (q.y -b) * d > q.x * b;
  return select(length(q - vec2f(-d, 0.))-r, length(q - vec2f(0., b)), cond);
}

Moon - exact

fn sdMoon(p: vec2f, d: f32, ra: f32, rb: f32) -> f32 {
  let q = vec2f(p.x, abs(p.y));
  let a = (ra * ra - rb * rb + d * d) / (2. * d);
  let b = sqrt(max(ra * ra - a * a, 0.));
  if (d * (q.x * b - q.y * a) > d * d * max(b - q.y, 0.)) { return length(q-vec2f(a, b)); }
  return max((length(q) - ra), -(length(q - vec2f(d, 0.)) - rb));
}

Rounded Cross - exact

fn sdRoundedCross(p: vec2f, h: f32) -> f32 {
  let k = 0.5 * (h + 1. / h);
  let q = abs(p);
  let v1 = q - vec2f(1., k);
  let v2 = q - vec2f(0., h);
  let v3 = q - vec2f(1., 0.);
  let d1 = k - sqrt(dot(v1, v1));
  let d2 = sqrt(min(dot(v2, v2), dot(v3, v3)));
  return select(d2, d1, q.x < 1. && q.y < q.x * (k - h) + h);
}

Egg - exact

fn sdEgg(p: vec2f, ra: f32, rb: f32) -> f32 {
  let k = sqrt(3.);
  let q = vec2f(abs(p.x), p.y);
  let r = ra - rb;
  let d1 = length(q) - r;
  let d2 = length(vec2f(q.x,  q.y - k * r));
  let d3 = length(vec2f(q.x + r, q.y)) - 2. * r;
  return select(select(d3, d2, k * (q.x + r) < q.y), d1, q.y < 0.) - rb;
}

Heart - exact

fn sdHeart(p: vec2f) -> f32 {
  let q = vec2f(abs(p.x), p.y);
  let w = q - vec2f(0.25, 0.75);
  if (q.x + q.y > 1.0) { return sqrt(dot(w, w)) - sqrt(2.) / 4.; }
  let u = q - vec2f(0., 1.);
  let v = q - 0.5 * max(q.x + q.y, 0.);
  return sqrt(min(dot(u, u), dot(v, v))) * sign(q.x - q.y);
}

Cross - exact exterior, bound interior

fn sdCross(p: vec2f, b: vec2f) -> f32 {
  var q: vec2f = abs(p);
  q = select(q.xy, q.yx, q.y > q.x);
  let t = q - b;
  let k = max(t.y, t.x);
  let w = select(vec2f(b.y - q.x, -k), t, k > 0.);
  return sign(k) * length(max(w, vec2f(0.)));
}

Rounded X - exact

fn sdRoundedX(p: vec2f, w: f32, r: f32) -> f32 {
  let q = abs(p);
  return length(q - min(q.x + q.y, w) * 0.5) - r;
}

Polygon - exact

const N: i32 = 5;
fn sdPolygon(p: vec2f, v: ptr<function, array<vec2f, 5>>) -> f32 {
  let c = *v;
  var d = dot(p - c[0], p - c[0]);
  var s: f32 = 1.;
  for (var i: i32 = 0; i < N; i = i + 1) {
    let j = (i + 1) % N;
    let e = c[i] - c[j];
    let w = p - c[j];
    let b = w - e * clamp(dot(w, e) / dot(e, e), 0., 1.);
    d = min(d, dot(b, b));
    let c1 = p.y >= c[j].y;
    let c2 = p.y < c[i].y;
    let c3 = e.x * w.y > e.y * w.x;
    let c = vec3<bool>(c1, c2, c3);
    if (all(c) || all(!c)) { s = -s; };
  }
  return s * sqrt(d);
}

Ellipse - exact

fn sdEllipse(p: vec2f, ab: vec2f) -> f32 {
  var q: vec2f = abs(p);
  var e: vec2f = ab;
  if (q.x > q.y) {
    q = q.yx;
    e = ab.yx;
  }
  let l = e.y * e.y - e.x * e.x;
  let m = e.x * q.x / l;
  let m2 = m * m;
  let n = e.y * q.y / l;
  let n2 = n * n;
  let c = (m2 + n2 - 1.) / 3.;
  let c3 = c * c * c;
  let b = c3 + m2 * n2 * 2.;
  let d = c3 + m2 * n2;
  let g = m + m * n2;
  var co: f32;
  if (d < 0.) {
    let h = acos(b / c3) / 3.0;
    let s = cos(h);
    let t = sin(h) * sqrt(3.);
    let rx = sqrt(-c * (s + t + 2.0) + m2);
    let ry = sqrt(-c * (s - t + 2.0) + m2);
    co = (ry + sign(l) * rx + abs(g) / (rx * ry) - m) / 2.;
  } else {
    let h = 2. * m * n * sqrt(d);
    let s = sign(b + h) * pow(abs(b + h), 1. / 3.);
    let u = sign(b - h) * pow(abs(b - h), 1. / 3.);
    let rx = -s - u - c * 4. + 2. * m2;
    let ry = (s - u) * sqrt(3.);
    let rm = sqrt(rx * rx + ry * ry);
    co = (ry / sqrt(rm - rx) + 2. * g / rm - m) / 2.;
  }
  let r = e * vec2f(co, sqrt(1.0-co*co));
  return length(r - q) * sign(q.y - r.y);
}

Parabola - exact

fn sdParabola(pos: vec2f, k: f32) -> f32 {
  let p = vec2f(abs(pos.x), pos.y);
  let ik = 1. / k;
  let u = ik * (p.y - 0.5 * ik) / 3.;
  let v = 0.25 * ik * ik * p.x;
  let h = v * v - u * u * u;
  let r = sqrt(abs(h));
  let x = select(2. * cos(atan2(r, v) / 3.) * sqrt(u),
    pow(v + r, 1. / 3.) - pow(abs(v - r), 1. / 3.) * sign(r - v),
    h > 0.0);
  return length(p - vec2f(x, k * x * x)) * sign(p.x - x);
}

Parabola Segment - exact

fn sdParabolaSegment(pos: vec2f, wi: f32, he: f32) -> f32 {
  let p = vec2f(abs(pos.x), pos.y);
  let ik = wi * wi / he;
  let u = ik * (he - p.y - 0.5 * ik) / 3.;
  let v = p.x * ik * ik * 0.25;
  let h = v * v - u * u * u;
  let r = sqrt(abs(h));
  var x: f32 = select(2. * cos(atan(r / v) / 3.) * sqrt(u),
    pow(v + r, 1. / 3.) - pow(abs(v - r), 1. / 3.) * sign(r - v),
    h > 0.0);
  x = min(x, wi);
  return length(p - vec2f(x, he - x * x / ik)) * sign(ik * (p.y - he) + p.x * p.x);
}

Quadratic Bezier - exact

fn sdBezier(p: vec2f, A: vec2f, B: vec2f, C: vec2f) -> vec2f {
  let a = B - A;
  let b = A - 2. * B + C;
  let c = a * 2.;
  let d = A - p;
  let kk = 1. / dot(b, b);
  let kx = kk * dot(a, b);
  let ky = kk * (2. * dot(a, a) + dot(d, b)) / 3.;
  let kz = kk * dot(d, a);

  let p1 = ky - kx * kx;
  let p3 = p1 * p1 * p1;
  let q = kx * (2.0 * kx * kx - 3.0 * ky) + kz;
  var h: f32 = q * q + 4. * p3;

  var res: vec2f;
  if (h >= 0.) {
    h = sqrt(h);
    let x = (vec2f(h, -h) - q) / 2.;
    let uv = sign(x) * pow(abs(x), vec2f(1. / 3.));
    let t = clamp(uv.x + uv.y - kx, 0., 1.);
    let f = d + (c + b * t) * t;
    res = vec2f(dot(f, f), t);
  } else {
    let z = sqrt(-p1);
    let v = acos(q / (p1 * z * 2.)) / 3.;
    let m = cos(v);
    let n = sin(v) * 1.732050808;
    let t = clamp(vec2f(m + m, -n - m) * z - kx, vec2f(0.0), vec2f(1.0));
    let f = d + (c + b * t.x) * t.x;
    var dis: f32 = dot(f, f);
    res = vec2f(dis, t.x);

    let g = d + (c + b * t.y) * t.y;
    dis = dot(g, g);
    res = select(res, vec2f(dis, t.y), dis < res.x);
  }
  res.x = sqrt(res.x);
  return res;
}

Bobbly Cross - exact

fn sdBlobbyCross(pos: vec2f, he: f32) -> f32 {
  var p: vec2f = abs(pos);
  p = vec2f(abs(p.x - p.y), 1. - p.x - p.y) / sqrt(2.);

  let u = (he - p.y - 0.25 / he) / (6. * he);
  let v = p.x / (he * he * 16.);
  let h = v * v - u * u * u;

  var x: f32; var y: f32;
  if (h > 0.) {
    let r = sqrt(h);
    x = pow(v + r, 1. / 3.) - pow(abs(v - r), 1. / 3.) * sign(r - v);
  } else {
    let r = sqrt(u);
    x = 2. * r * cos(acos(v / (u * r)) / 3.);
  }
  x = min(x, sqrt(2.) / 2.);

  let z = vec2f(x, he * (1. - 2. * x * x)) - p;
  return length(z) * sign(z.y);
}

MIT License. © 2023 Inigo Quilez, Munrocket

@philholden
Copy link
Copy Markdown

I'd love to play round with WebGPU. What is the boilerplate needed to produce these images?

@munrocket
Copy link
Copy Markdown
Author

Here online example on compute.toys and hello webgpu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment