Back openDesk Edu for a sovereign, open-source education â every vote counts.
Vote nowSave products you love by clicking the heart icon.
Learn to implement hand tracking in WebXR applications using the Hand Tracking API, including gesture recognition and accessibility considerations for immersive experiences.
Spatial UI geht ĂŒber flache Bildschirme hinaus in 3D-Umgebungen, in denen Benutzer mit Inhalten ĂŒber ihre HĂ€nde, den Blick und Controller interagieren. Das Design fĂŒr dieses Medium erfordert ein grundlegendes Ăberdenken von Layout, Typografie, Feedback und Barrierefreiheit.
Dieser Leitfaden behandelt bewĂ€hrte Muster fĂŒr den Aufbau rĂ€umlicher Interfaces in WebXR, ergĂ€nzt durch praktische Code-Beispiele und Designprinzipien aus Produktionsanwendungen.
| Screen UI | Spatial UI |
|---|---|
| Fixer Viewport | Benutzer bewegt sich um Inhalte |
| Maus-/Touch-Input | HĂ€nde, Blick, Controller, Sprache |
| CSS-Pixel | Meter der realen Welt |
| 2D-Layout | 3D-Platzierung mit Tiefe |
| Keine physische ErmĂŒdung | Komfort und Ergonomie zĂ€hlen |
Die Grundregel: Spatial UI sollte sich in der realen Welt natĂŒrlich anfĂŒhlen, nicht wie ein flacher Bildschirm, der in VR schwebt.
Verwenden Sie Einheiten der realen Welt fĂŒr das rĂ€umliche Layout:
// 1 meter â 1 unit in WebXR
const UI_DISTANCE = 1.5; // arm's reach
const BUTTON_SIZE = 0.08; // 8cm button
const TEXT_HEIGHT = 0.03; // 3cm text for readability
Ein Button, der in ArmlÀnge kleiner als 5 cm ist, lÀsst sich schwer antippen. Text unter 2 cm wird unleserlich. Testen Sie Ihre Layouts in realen Entfernungen.
Strukturieren Sie Inhalte in Zonen basierend auf der Aufmerksamkeit des Benutzers:
// Spatial zones helper
const ZONES = {
focal: { x: 0, y: 0, z: -1.5 },
peripheral: { x: 1.2, y: 0.3, z: -1.5 },
background: { x: 0, y: 0, z: -5 },
};
Verwenden Sie ein curved billboard fĂŒr Textpanels, sodass jedes Wort den gleichen Abstand zum Benutzer hat:
const textPanel = new THREE.Mesh(
new THREE.CylinderGeometry(0.8, 0.8, 0.5, 32, 1, true),
textMaterial,
);
textPanel.rotation.x = -Math.PI / 2;
FĂŒr Dashboards und datenintensive UIs empfiehlt sich ein scrollbares Panel, zu dem der Benutzer herausgreift, um zu interagieren, Ă€hnlich wie bei einem physischen Tablet.
Benutzer sollten Objekte greifen, tippen und ziehen können, wie sie es in der realen Welt tun wĂŒrden:
// Three.js drag interaction
controller.addEventListener("select", () => {
const hit = raycast(intersectables);
if (hit) {
hit.object.userData.onSelect?.();
}
});
controller.addEventListener("squeeze", () => {
const grabbed = raycast(grabbables);
if (grabbed) {
attachToController(grabbed.object, controller);
}
});
Nutzen Sie bei Hand-Tracking den Blick (Gaze) zum Anvisieren und das Zusammenkneifen der Finger (Pinch) zur BestÀtigung:
if (hand.pinchStrength > 0.8) {
const target = gazeRaycast(scene.children);
if (target) target.userData.onPinch?.();
}
Dieses Muster ist ermĂŒdungsfrei und funktioniert gut auf GerĂ€ten ohne Controller (Quest Hand-Tracking, Vision Pro).
| Modus | Anwendungsfall | Implementierung |
|---|---|---|
| World-locked | MenĂŒs, HUDs, Labels | XRSpace mit Anker |
| Body-locked | Toolbars, Inventar | Folgt Kamera-XZ-Position |
| Device-locked | Benachrichtigungen, Warnungen | Overlay-Canvas (selten) |
RĂ€umliche Interaktionen benötigen ein starkes visuelles Feedback, da Benutzer keine physischen Tasten spĂŒren können:
function InteractionFeedback({ hovered, selected, disabled }: FeedbackProps) {
const scale = hovered ? 1.05 : 1;
const color = selected ? "#4ade80" : disabled ? "#6b7280" : "#3b82f6";
const opacity = disabled ? 0.4 : 1;
return (
<mesh scale={scale}>
<planeGeometry args={[0.2, 0.08]} />
<meshStandardMaterial color={color} transparent opacity={opacity} />
</mesh>
);
}
Bieten Sie immer mindestens drei Feedback-ZustĂ€nde an: idle â hover â active.
Jede Interaktion sollte eine akustische Antwort haben:
function playInteractionSound(type: "hover" | "select" | "error") {
const buffers = {
hover: audioBufferHover,
select: audioBufferClick,
error: audioBufferError,
};
const source = audioContext.createBufferSource();
source.buffer = buffers[type];
source.connect(audioContext.destination);
source.start();
}
Nicht alle Benutzer können in VR klar sehen. UnterstĂŒtzen Sie:
// Reduce motion for sensitive users
const userPrefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const ANIMATION_DURATION = userPrefersReducedMotion ? 0 : 300;
Spatial UI muss innerhalb des Frame-Budgets bleiben:
| Metrik | Budget |
|---|---|
| Draw Calls (nur UI) | †20 |
| UI-Triangles | †10.000 |
| UI-Texturspeicher | †32 MB |
| Update-Zeit | †2 ms |
Verwenden Sie instanced meshes fĂŒr wiederkehrende Elemente (Buttons, Labels):
const buttonInstances = new THREE.InstancedMesh(buttonGeometry, material, MAX_BUTTONS);
Eine minimale Spatial-UI-Komponente:
export function SpatialButton({ position, label, onSelect, disabled }: SpatialButtonProps) {
return (
<group position={position}>
<mesh onClick={onSelect} onPointerOver={() => playInteractionSound("hover")}>
<planeGeometry args={[0.2, 0.08]} />
<meshStandardMaterial
color={disabled ? "#6b7280" : "#3b82f6"}
transparent
opacity={disabled ? 0.4 : 1}
/>
</mesh>
<SpatialText text={label} position={[0, 0, 0.001]} fontSize={0.025} />
</group>
);
}
Das Design von Spatial UI ist noch eine aufstrebende Disziplin. Testen Sie frĂŒh, testen Sie oft und priorisieren Sie den Benutzerkomfort vor dem visuellen Feinschliff.