v147 · WebXR · AR
Compatibility Lab
Probes all five WebXR plane detection globals — XRPlane, XRPlaneSet, XRFrame.detectedPlanes, navigator.xr presence, and immersive-ar session support. Runs isSessionSupported() live and provides the graceful-degradation pattern for browsers without plane detection.
API probes
Plane Detection API compatibility matrix
| API / Global | Chrome ≤146 (desktop) | Chrome 147 (Android AR) | Notes |
|---|---|---|---|
| navigator.xr | ✓ | ✓ | WebXR Device API entry point |
| XRPlane | ✗ | ✓ | Represents a detected real-world plane |
| XRFrame.detectedPlanes | ✗ | ✓ | XRPlaneSet updated each frame |
| XRPlane.orientation | ✗ | ✓ | "horizontal" | "vertical" |
| XRPlane.polygon | ✗ | ✓ | DOMPointReadOnly[] — convex hull |
| immersive-ar (isSessionSupported) | Device-dependent | ✓ with ARCore | Requires Android + ARCore |
| plane-detection feature string | ✗ | ✓ | Pass in optionalFeatures array |
Live session support probe
navigator.xr + plane detection globals
Click "Run probe" to check WebXR plane detection support…
Graceful degradation pattern
/* WebXR Plane Detection — feature detection + fallback */
async function startARWithPlanes() {
/* 1. Check WebXR exists */
if (!navigator.xr) {
showFallbackUI('WebXR not supported');
return;
}
/* 2. Check immersive-ar session support */
const arSupported = await navigator.xr.isSessionSupported('immersive-ar');
if (!arSupported) {
showFallbackUI('Immersive AR not supported on this device');
return;
}
/* 3. Check plane detection globals (Chrome 147+) */
const hasPlaneDetection = 'XRPlane' in window &&
'detectedPlanes' in XRFrame.prototype;
/* 4. Request session — plane-detection as optional feature */
const session = await navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['local'],
optionalFeatures: ['plane-detection'],
});
/* 5. In the frame loop, check if plane detection was granted */
session.requestAnimationFrame((time, frame) => {
if (frame.detectedPlanes) {
/* Plane detection available — use plane surfaces */
for (const plane of frame.detectedPlanes) {
const pose = frame.getPose(plane.planeSpace, referenceSpace);
renderPlaneOverlay(plane.orientation, plane.polygon, pose);
}
} else {
/* Graceful fallback — no plane detection, place content manually */
renderWithHitTest(frame);
}
});
}
/* Check plane detection support without requesting a session */
function supportsPlaneDetection() {
return 'XRPlane' in window &&
typeof XRFrame !== 'undefined' &&
'detectedPlanes' in XRFrame.prototype;
}
see also
- Capability Inspector — live WebXR and plane API probe
- Back to feature index
- ChromeStatus entry