@vshaders/sdf

Signed distance field primitives and operators as pure WGSL modules. A signed distance function returns how far a point is from a shape's boundary: negative inside, zero on the boundary, positive outside, in the same units as p. Because distances are just numbers, shapes combine with plain math: union is min, intersection is max, and the smooth variants blend between them. Every module is functions only, no bindings, no entry points, so vgpu's resolver prunes whatever you don't import.

npm install @vshaders/sdf

@vshaders/sdf/2d

Shapes centered at the origin; translate by evaluating at (p - center).

SignatureDescription
sdCircle(p: vec2f, radius: f32) -> f32Circle of the given radius.
sdBox2(p: vec2f, halfSize: vec2f) -> f32Box with half-extents halfSize. Exact distance, including corners.
sdRoundedBox2(p: vec2f, halfSize: vec2f, cornerRadius: f32) -> f32Rounded box. halfSize is the outer half-extent; cornerRadius must not exceed min(halfSize.x, halfSize.y).
sdSegment2(p: vec2f, start: vec2f, end: vec2f) -> f32Distance to the line segment. A zero-length segment degrades to distance to a point.
sdRing(p: vec2f, radius: f32, halfThickness: f32) -> f32Annulus around the origin.
sdHalfPlane(p: vec2f, normal: vec2f, offset: f32) -> f32Half plane. normal must be unit length; offset is the plane's signed distance from the origin.
ring.wgsl
import { sdRing } from "@vshaders/sdf/2d";

@fragment fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
  let p = position.xy / 512.0 - vec2f(0.5);
  let d = sdRing(p, 0.3, 0.02);
  return vec4f(vec3f(smoothstep(0.01, -0.01, d)), 1.0);
}

@vshaders/sdf/3d

Same conventions as 2D: origin-centered, translate by offsetting p.

SignatureDescription
sdSphere(p: vec3f, radius: f32) -> f32Sphere of the given radius.
sdBox3(p: vec3f, halfSize: vec3f) -> f32Box. Exact distance, including edges and corners.
sdRoundedBox3(p: vec3f, halfSize: vec3f, cornerRadius: f32) -> f32Rounded box; the same cornerRadius bound as the 2D version.
sdTorus(p: vec3f, majorRadius: f32, minorRadius: f32) -> f32Ring in the XZ plane. majorRadius is the ring's radius, minorRadius the tube's.
sdCapsule3(p: vec3f, start: vec3f, end: vec3f, radius: f32) -> f32Capsule between two points. A zero-length capsule degrades to a sphere at start.
sdCylinderY(p: vec3f, radius: f32, halfHeight: f32) -> f32Finite cylinder along the Y axis.
sdPlane(p: vec3f, normal: vec3f, offset: f32) -> f32Plane. normal must be unit length; offset is the plane's signed distance from the origin.
torus-slice.wgsl
import { sdTorus } from "@vshaders/sdf/3d";

@fragment fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
  let p = position.xy / 512.0 - vec2f(0.5);
  // A slice of the 3D field through the plane y = 0.
  let d = sdTorus(vec3f(p.x, 0.0, p.y), 0.3, 0.1);
  return vec4f(vec3f(smoothstep(0.01, -0.01, d)), 1.0);
}

@vshaders/sdf/ops

Boolean operators take distances already evaluated at the same point. Smooth variants blend over a band of smoothness world units; smoothness <= 0 degrades to the sharp operator instead of dividing by zero.

SignatureDescription
opUnion(d1: f32, d2: f32) -> f32Union: the minimum of the two distances.
opIntersect(d1: f32, d2: f32) -> f32Intersection: the maximum of the two distances.
opSubtract(base: f32, cut: f32) -> f32Removes cut from base.
opSmoothUnion(d1: f32, d2: f32, smoothness: f32) -> f32Polynomial smooth minimum.
opSmoothIntersect(d1: f32, d2: f32, smoothness: f32) -> f32Smooth intersection.
opSmoothSubtract(base: f32, cut: f32, smoothness: f32) -> f32Smooth subtraction.
opRound(d: f32, radius: f32) -> f32Grow the shape outward by radius, rounding its corners.
opOnion(d: f32, halfThickness: f32) -> f32Hollow the shape into a shell of the given half-thickness.
repeat2(p: vec2f, period: vec2f) -> vec2fInfinite domain repetition in 2D. Apply to p before evaluating a distance function.
repeat3(p: vec3f, period: vec3f) -> vec3fThe 3D counterpart.

Repetition guarantees: a period component <= 0 leaves that axis unrepeated, and the repeated field is only exact for shapes smaller than half the period.

blobs.wgsl
import { sdCircle } from "@vshaders/sdf/2d";
import { opSmoothUnion } from "@vshaders/sdf/ops";

@fragment fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
  let p = position.xy / 512.0 - vec2f(0.5);
  let d = opSmoothUnion(
    sdCircle(p - vec2f(0.15, 0.0), 0.2),
    sdCircle(p + vec2f(0.15, 0.0), 0.2),
    0.1
  );
  return vec4f(vec3f(smoothstep(0.01, -0.01, d)), 1.0);
}

Verify

npx vgpu check shaders/your-entry.wgsl --require-validation

Runs on the entry shader that imports these modules: it resolves the import graph, validates the composed shader against a real device, and prints its reflection.

Provenance

The exact box, sphere, segment, torus and cylinder distances and the polynomial smooth minimum are standard published constructions from the computer graphics literature (Hart's sphere tracing lineage; the smooth minimum polynomial as popularized in Inigo Quilez's articles). The WGSL here is an original transcription of those formulas, with explicit guards (zero-length segments, non-positive smoothness or period) in the argument ranges the formulas leave undefined. MIT licensed.