Signatur
Beschreibung
XRJointSpace gehört zum WebXR Hand Input Module. Für jedes der 25 Gelenke einer getrackten Hand gibt es ein eigenes XRJointSpace-Objekt, etwa für das Handgelenk ("wrist"), die Fingerspitze des Zeigefingers ("index-finger-tip") oder das Daumengrundgelenk ("thumb-metacarpal"). Diese Objekte bekommst du über die XRHand-Map, z. B. mit inputSource.hand.get("index-finger-tip").
Weil XRJointSpace von XRSpace erbt, lässt es sich überall dort einsetzen, wo ein Raum erwartet wird. Die eigentliche Pose eines Gelenks fragst du pro Frame mit XRFrame.getJointPose(jointSpace, referenceSpace) ab. Das Ergebnis ist ein XRJointPose, der neben transform auch einen radius enthält, also den ungefähren Radius des Gelenks in Metern. Für viele Gelenke auf einmal sind XRFrame.fillPoses() und XRFrame.fillJointRadii() deutlich schneller.
Das Interface hat genau eine eigene Eigenschaft:
jointName(schreibgeschützt,XRHandJoint-String): der Name des Gelenks, z. B."middle-finger-phalanx-distal".
Einen Konstruktor gibt es nicht. Der Browser erzeugt die Instanzen, sobald eine Sitzung mit dem Feature "hand-tracking" läuft und eine Eingabequelle eine Hand liefert.
Rückgabewert
XRHand.get(jointName) oder beim Iterieren über eine XRHand geliefert. Eigene Eigenschaft: jointName: XRHandJoint.Beispiele
Hand-Tracking-Sitzung starten und Gelenk-Räume auflisten
const session = await navigator.xr.requestSession('immersive-vr', {
optionalFeatures: ['hand-tracking']
});
session.addEventListener('inputsourceschange', () => {
for (const source of session.inputSources) {
if (!source.hand) continue; // Controller statt Hand
// XRHand ist eine Map: jointName -> XRJointSpace
for (const [name, jointSpace] of source.hand) {
console.log(source.handedness, name, jointSpace instanceof XRJointSpace);
}
}
});
Position der Zeigefingerspitze pro Frame auslesen
const refSpace = await session.requestReferenceSpace('local-floor');
const onFrame = (time, frame) => {
for (const source of frame.session.inputSources) {
const tip = source.hand?.get('index-finger-tip');
if (!tip) continue;
const pose = frame.getJointPose(tip, refSpace);
if (pose) {
const { x, y, z } = pose.transform.position;
console.log(`${tip.jointName}: ${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)} (r=${pose.radius.toFixed(3)} m)`);
}
}
frame.session.requestAnimationFrame(onFrame);
};
session.requestAnimationFrame(onFrame);
Pinch-Geste erkennen (Daumen- und Zeigefingerspitze)
const isPinching = (frame, hand, refSpace) => {
const thumb = frame.getJointPose(hand.get('thumb-tip'), refSpace);
const index = frame.getJointPose(hand.get('index-finger-tip'), refSpace);
if (!thumb || !index) return false;
const a = thumb.transform.position;
const b = index.transform.position;
const dist = Math.hypot(a.x - b.x, a.y - b.y, a.z - b.z);
return dist < 0.015; // unter 1,5 cm gilt als Pinch
};
Alle Gelenk-Posen gebündelt mit fillPoses()
const onFrame = (time, frame) => {
for (const source of frame.session.inputSources) {
if (!source.hand) continue;
const joints = [...source.hand.values()]; // XRJointSpace[]
const matrices = new Float32Array(joints.length * 16);
const ok = frame.fillPoses(joints, refSpace, matrices);
if (ok) {
// matrices enthält 25 4×4-Matrizen, z. B. für Instanced Rendering
}
}
frame.session.requestAnimationFrame(onFrame);
};
// Wichtig · Fallstricke
- Feature anfordern: Ohne
"hand-tracking"inrequiredFeaturesoderoptionalFeaturesbleibtinputSource.handnull, und es gibt keineXRJointSpace-Objekte. - Pose kann fehlen:
getJointPose()liefertnull, wenn ein Gelenk gerade nicht getrackt wird, z. B. weil es verdeckt ist oder außerhalb des Kamerasichtfelds liegt. Prüfe das deshalb in jedem Frame.fillPoses()gibt in diesem Fallfalsezurück. - Ausrichtung: Die Y-Achse eines Gelenk-Raums zeigt senkrecht zur Hautoberfläche, die −Z-Achse entlang des Knochens in Richtung Fingerspitze.
- Performance: Wenn du alle 25 Gelenke brauchst, ist
fillPoses()schneller als 25 einzelnegetJointPose()-Aufrufe. - Datenschutz: Handdaten eignen sich zum Fingerprinting. Browser verlangen deshalb eine Nutzerzustimmung und runden die Werte teilweise.
- Kompatibilität: Nur in sicheren Kontexten (HTTPS) und nur auf Geräten mit Hand-Tracking verfügbar, etwa Meta Quest oder Apple Vision Pro. Desktop-Browser ohne XR-Hardware kennen das Interface meist nicht. Prüfe also vorher mit
'XRJointSpace' in window.