Develop

Hand tracking unextrapolated poses OpenXR extension

Updated: Apr 24, 2026
This topic provides:
  • A usage guide for the Hand Tracking Unextrapolated Poses OpenXR extension and links to related topics.

Overview

By default, xrLocateHandJointsEXT returns hand poses extrapolated to the requested display time. The runtime predicts hand positions forward in time to reduce perceived latency between tracking and rendering.
The XR_META_hand_tracking_unextrapolated_poses extension provides access to the latest calculated hand poses without temporal extrapolation. When this extension is active, the returned joint locations reflect the actual tracked data at the time the tracking system captured it, rather than a prediction of where the hands will be at the requested display time.
Applications that implement custom prediction algorithms or require raw tracking data for latency-sensitive processing can use this extension to bypass the runtime’s built-in extrapolation.
The extension also provides a captureTime value indicating when the tracking data was captured. Applications can use this timestamp to understand the age of the tracking data relative to the predicted display time.

Extension: XR_META_hand_tracking_unextrapolated_poses

Extension name: XR_META_hand_tracking_unextrapolated_poses
Spec version: 1
Extension type: instance
Depends on: XR_EXT_hand_tracking

XrHandTrackingUnextrapolatedPosesRequestMETA

This struct requests unextrapolated hand poses from the runtime. Chain it into XrHandJointsLocateInfoEXT::next when calling xrLocateHandJointsEXT.
typedef struct XrHandTrackingUnextrapolatedPosesRequestMETA {
    XrStructureType             type;
    const void* XR_MAY_ALIAS    next;
} XrHandTrackingUnextrapolatedPosesRequestMETA;
FieldTypeDescription
type
XrStructureType
Must be XR_TYPE_HAND_TRACKING_UNEXTRAPOLATED_POSES_REQUEST_META
next
const void*
NULL or pointer to the next structure in the chain
When this struct is chained to XrHandJointsLocateInfoEXT, the runtime provides the latest calculated hand poses without temporal extrapolation. The time parameter in XrHandJointsLocateInfoEXT is still used for determining the reference space transformation. However, the runtime ignores it for determining the hand joint locations.

XrHandTrackingUnextrapolatedPosesMETA

This struct receives the capture timestamp of the unextrapolated tracking data. Chain it into XrHandJointLocationsEXT::next when calling xrLocateHandJointsEXT.
typedef struct XrHandTrackingUnextrapolatedPosesMETA {
    XrStructureType       type;
    void* XR_MAY_ALIAS    next;
    XrTime                captureTime;
} XrHandTrackingUnextrapolatedPosesMETA;
FieldTypeDescription
type
XrStructureType
Must be XR_TYPE_HAND_TRACKING_UNEXTRAPOLATED_POSES_META
next
void*
NULL or pointer to the next structure in the chain
captureTime
XrTime
Timestamp of when the tracking data used to generate the hand poses was captured
This struct is optional. If you do not need the capture timestamp, chain only the request struct.

Chaining pattern

The two structs chain into opposite sides of the xrLocateHandJointsEXT call:
  • Input: Chain XrHandTrackingUnextrapolatedPosesRequestMETA into XrHandJointsLocateInfoEXT::next
  • Output: Chain XrHandTrackingUnextrapolatedPosesMETA into XrHandJointLocationsEXT::next
Note: Other extensions such as XrHandJointVelocitiesEXT, XrHandTrackingScaleFB, XrHandTrackingAimStateFB, and XrHandTrackingDataSourceStateEXT also chain into XrHandJointLocationsEXT::next. Multiple extensions can be chained together.

Example Usage

The following example requests unextrapolated hand poses and retrieves the capture timestamp:
XrHandTrackingUnextrapolatedPosesRequestMETA unextrapolatedPosesRequestMETA{
    XR_TYPE_HAND_TRACKING_UNEXTRAPOLATED_POSES_REQUEST_META};

XrHandJointsLocateInfoEXT locateInfo{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT};
locateInfo.baseSpace = worldSpace;
locateInfo.time = frameState.predictedDisplayTime;
locateInfo.next = &unextrapolatedPosesRequestMETA;

XrHandTrackingUnextrapolatedPosesMETA unextrapolatedPosesMETA{
    XR_TYPE_HAND_TRACKING_UNEXTRAPOLATED_POSES_META};

XrHandJointLocationsEXT locations{XR_TYPE_HAND_JOINT_LOCATIONS_EXT};
XrHandJointLocationEXT jointLocations[XR_HAND_JOINT_COUNT_EXT];
locations.jointCount = XR_HAND_JOINT_COUNT_EXT;
locations.jointLocations = jointLocations;
locations.next = &unextrapolatedPosesMETA;

CHK_XR(xrLocateHandJointsEXT(handTracker, &locateInfo, &locations));
After the call, unextrapolatedPosesMETA.captureTime contains the timestamp when the tracking system captured the data. To calculate the age of the tracking data in seconds:
// XrTime is in nanoseconds; divide by 1e9 to convert to seconds
float deltaTime = (frameState.predictedDisplayTime - unextrapolatedPosesMETA.captureTime) / 1e9f;
To conditionally toggle between extrapolated and unextrapolated poses at runtime, set locateInfo.next based on a flag:
if (useUnextrapolated) {
    locateInfo.next = &unextrapolatedPosesRequestMETA;
} else {
    locateInfo.next = nullptr;
}