facebook/iwsdk-depth-occlusion
Guide for implementing depth sensing and occlusion in IWSDK projects. Use when adding depth-based occlusion to hide virtual objects behind real-world surfaces, configuring DepthSensingSystem, choosing occlusion modes, or troubleshooting objects that disappear or fail to occlude.
npx skills add https://github.com/facebook/immersive-web-sdk --skill iwsdk-depth-occlusion
Hide virtual objects behind real-world surfaces using WebXR depth sensing. The system samples a per-pixel depth texture from the XR device and compares it against each virtual fragment's depth — if the real surface is closer, the fragment is faded out.
Three things are required: XR session depth config, the system, and the component.
// iwsdk.config.json
{
"world": {
"xr": {
"mode": "ar",
"referenceSpace": "unbounded",
"features": {
"depthSensing": {
"required": true,
"usage": "gpu-optimized",
"format": "float32"
},
"hitTest": { "required": true },
"anchors": { "required": true },
"unbounded": { "required": true }
}
}
}
}
DepthSensingSystem and DepthOccludableimport { DepthSensingSystem, DepthOccludable } from '@iwsdk/core';
world
.registerSystem(DepthSensingSystem, {
configData: {
enableDepthTexture: true,
enableOcclusion: true,
useFloat32: true,
blurRadius: 20.0,
},
});
DepthOccludable to entitiesimport { DepthOccludable, OcclusionShadersMode } from '@iwsdk/core';
// Soft occlusion (default) — smooth edges via 13-tap blur
entity.addComponent(DepthOccludable);
// Hard occlusion — sharp edges, single depth sample
entity.addComponent(DepthOccludable, {
mode: OcclusionShadersMode.HardOcclusion,
});
// MinMax occlusion — best quality, extra preprocessing pass
entity.addComponent(DepthOccludable, {
mode: OcclusionShadersMode.MinMaxSoftOcclusion,
});
The material must have transparent: true. The system sets this automatically, but verify it on custom materials.
| Mode | Quality | Cost | Best For |
| --------------------- | ------- | ------ | ----------------------------------------------------------------- |
| SoftOcclusion | Good | Low | Most objects — smooth edges, hides depth aliasing |
| HardOcclusion | Basic | Lowest | Small objects or when sharp edges are acceptable |
| MinMaxSoftOcclusion | Best | Medium | Large objects with complex silhouettes against varied backgrounds |
| Property | Type | Default | Description |
| -------------------- | ------- | ------- | ----------------------------------------- |
| enableOcclusion | Boolean | true | Master switch for all occlusion |
| enableDepthTexture | Boolean | true | Create GPU textures from depth data |
| useFloat32 | Boolean | true | Float32 depth textures (higher precision) |
| blurRadius | Float32 | 20.0 | Blur radius for soft occlusion (pixels) |
| Mode | When to use |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| cpu-optimized | Simpler, works everywhere. Depth as linear meters in a DataArrayTexture. |
| gpu-optimized | Recommended. Matches Quest hardware format. Depth as reverse-Z inverse depth in an ExternalTexture. Required for production parity with on-device behavior. Note that Quest devices only support this mode. |
Depth occlusion only works in AR mode. The scene background must be null for passthrough:
scene.background = null;
Objects never occlude (always visible on top)
DepthOccludable componentDepthSensingSystem is registered with enableOcclusion: truedepthSensing is in the XR features configObjects always invisible in IWER
Flickering or noisy occlusion edges
blurRadius (try 30-40)HardOcclusion to SoftOcclusionMinMaxSoftOcclusion for best edge qualityworld.xr.mode to "ar" and configureworld.xr.features.depthSensing in iwsdk.config.json.
DepthOccludable may be incompatible with custom shaders that override diffuse or fog_vertex includes, since the occlusion code is injected at those shader hook points.DepthOccludable — they render normally on top of everything.DepthOccludable is built into IWSDK and is already registered — it appearsin scene_get_capabilities under registeredComponents. Do not call
world.registerComponent() for it; only the system needs registering.
Take facebook/iwsdk-depth-occlusion from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.