Skip to main content
Version: 3.5.x

Signed Distance Function (SDF)

Signed Distance Fields (SDFs) let you describe simple 3D geometry (like spheres, boxes, capsules, and planes) using a distance function instead of meshes. In our haptics stack, an SDF becomes a force field you can attach to a device session: when the cursor approaches (or enters) the shape, the system produces a normal force whose strength depends on distance, range, and easing.

This page documents the public API surface added/updated by the patch: the SDF Haptics effect payload, supported shapes, parameters, and request/response examples for creating, updating, listing, and deleting SDF effects.

Conceptual model

An SDF effect is:

  • A shape (e.g., sphere, box, capsule, plane)
  • A transform (position/rotation/scale of the effect in space)
  • A parameter object (params) that configures the selected shape (radii, endpoints, extents, normals, etc.)
  • Force controls:
    • force_scale — overall magnitude multiplier
    • range — thickness of the “active band” outside the surface where forces ramp down to zero
    • ease + reverse_easing — how force ramps with distance
  • Composition controls:
    • symmetry — how inside/outside is treated
    • blend — how multiple SDF effects combine

You can create multiple SDF effects per device; each effect has a unique id.

SDF Effect

{
"id": "string",
"transform": {
/* transform object */
},
"shape": "sphere | box | rounded_box | capsule | capsule_vertical | capped_cylinder | capped_cylinder_vertical | plane",
"params": {
/* shape parameters */
},
"force_scale": 1.0,
"range": 1.0,
"ease": "linear",
"reverse_easing": false,
"symmetry": "single | mirror | align",
"blend": "additive"
}

Field reference

FieldTypeRequiredMeaning
idstringUnique identifier for this effect within a device.
transformobjectPose of the effect in world/device space.
shapestringThe SDF primitive to evaluate.
paramsobjectShape-specific parameters (see below).
force_scalenumberMultiplies the final force magnitude. Default: 1.0.
rangenumberDistance outside the surface (in meters) where the effect is active. Default: 1.0.
easestringEasing curve applied over [0..range]. Default: linear.
reverse_easingbooleanIf true, flips easing direction. Default: false.
symmetrystringHow inside/outside is treated (see below). Default: single.
blendstringHow multiple SDF effects combine. Default: additive.

Shapes and parameters

All shapes are configured through a single params object. You can provide only the fields you need for the selected shape.

params object

{
"r": 1.0,
"h": 0.0,
"a": [0.0, 0.0, 0.0],
"b": [0.0, 0.0, 0.0],
"n": [0.0, 1.0, 0.0]
}

Tip: Values are interpreted in the effect’s local space (after applying transform).

Supported shapes (current)

Only the following SDF primitives are currently supported (see IQ’s SDF gallery for math background):

Shapeshape valueParameters used
Spherespherer
Boxboxb (half-extents)
Rounded boxrounded_boxb (half-extents), r (corner radius)
Capsule (segment)capsulea, b, r
Capsule (vertical)capsule_verticalh, r
Capped cylinder (segment)capped_cylindera, b, r
Capped cylinder (vertical)capped_cylinder_verticalh, r
Planeplanen, h

Notes on conventions

  • Vectors are arrays: a, b, n are 3D ([x,y,z]).
  • Box uses b as half-extents (e.g., b=[0.1,0.2,0.1]).
  • Plane uses n as the normal and h as offset along the normal.
  • Vertical variants use h as half-height (capsule/cylinder centered on origin along the local Y axis).

Symmetry

symmetry controls how the field behaves with respect to the surface:

  • single — Standard behavior. Inside vs. outside is preserved.
  • mirror — Uses the absolute distance to the surface and flips direction when “behind” the surface, creating mirrored behavior.
  • align — Uses the absolute distance without flipping direction (useful for “always push the same way” effects).

Easing and range

The force magnitude ramps with distance from the surface:

  • At the surface (distance = 0): maximum magnitude (subject to easing)
  • At distance = range: magnitude becomes 0 (inactive)

ease defines the curve of the falloff. reverse_easing inverts the curve.


Commands

  • "set_sdf": create/update localized SDF effects (array of items)
  • "remove_sdf": remove effects by id (array of ids)

Set / Update SDF Effect

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_sdf": [
{
"id": "boundary_049D",
"transform": {
"position": {
"x": 0,
"y": -0.05,
"z": -0.04
},
"rotation": {
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"scale": {
"x": 0.15,
"y": 0.1,
"z": 0.1
}
},
"shape": "sphere",
"force_scale": -2.0,
"range": 0.1,
"ease": "cubicInOut",
"reverse_easing": false,
"symmetry": "single",
"blend": "additive",
"params": {
"r": 1.0
}
}
]
}
}
]
}

Remove an SDF Effect

{
"inverse3": [{
"device_id": "049D",
"commands": {
"remove_sdf": ["boundary_049D"]
}
}]
}
  • Each id in the array is removed if present.
  • Removing a non-existent id is treated as a no-op (safe to call).

REST API

The SDF endpoints follow the same session/device routing conventions as the rest of the haptics API.

  • A device can have many SDF effects.
  • Each effect is identified by its id.
  • You can manage effects individually or in bulk per device.

Routes

  • GET

    • /{device_type}/{device_id}/sdf?session_id=<sid> → device scope (list of SDFs)
    • /{device_type}/{device_id}/sdf/{hfx_id}?session_id=<sid> → single SDF
    • /{device_type}/*/sdf?session_id=<sid> → session scope (all devices)
    • /{device_type}/*/sdf (no session_id) → all sessions
  • POST

    • /{device_type}/{device_id}/sdf/{hfx_id}?session_id=<sid> body: sdf_hfx (create/update one)
    • /{device_type}/{device_id}/sdf?session_id=<sid> body: device_sdf_dto (replace or upsert many for device)
  • DELETE

    • /{device_type}/{device_id}/sdf?session_id=<sid> or .../sdf/{hfx_id}?session_id=<sid>
    • session_id=* supported for “clear all”.
info

Handlers validate device_type (inverse3 only), enforce session_id/device_id presence, and return 404 for missing session/device/effect.

Behavior highlights

  • GET routing covers: all → session → device → single effect depending on path/params.
  • POST enforces path/body id consistency for single-effect writes and merges/sets device-level SDFs when posting a list.
  • DELETE is idempotent and returns a standard ok envelope even if nothing was erased

Examples

Sphere “bubble” you can press into

{
"id": "bubble",
"transform": {
/* place it where you want */
},
"shape": "sphere",
"params": {
"r": 0.12
},
"force_scale": 1.0,
"range": 0.08,
"ease": "linear",
"reverse_easing": false,
"symmetry": "single",
"blend": "additive"
}

Rounded box “soft wall”

{
"id": "soft_wall",
"transform": {
/* oriented wall */
},
"shape": "rounded_box",
"params": {
"b": [
0.30,
0.02,
0.30
],
"r": 0.01
},
"force_scale": 0.9,
"range": 0.06,
"ease": "cubicInOut",
"reverse_easing": false,
"symmetry": "single",
"blend": "additive"
}

Segment capsule “rail”

{
"id": "rail",
"transform": {
/* position/rotate rail */
},
"shape": "capsule",
"params": {
"a": [
-0.15,
0.0,
0.0
],
"b": [
0.15,
0.0,
0.0
],
"r": 0.015
},
"force_scale": 0.7,
"range": 0.05,
"ease": "quadraticOut",
"reverse_easing": false,
"symmetry": "mirror",
"blend": "additive"
}

Plane “invisible boundary”

{
"id": "boundary",
"transform": {
/* place plane */
},
"shape": "plane",
"params": {
"n": [
1.0,
0.0,
0.0
],
"h": 0.0
},
"force_scale": 0.8,
"range": 0.10,
"ease": "linear",
"reverse_easing": false,
"symmetry": "single",
"blend": "additive"
}