elics ECS framework.elicsGamepadWrapper, including button states, axis navigation, and haptic feedback patternsrealmeasure directory. Install dependencies with npm install, then run npm run serve to launch the development server at https://localhost:8081. Open the URL in Meta Quest Browser and tap Launch to enter the mixed reality experience. For detailed build instructions, see the RealMeasure README.| File | What it demonstrates | Key concepts |
|---|---|---|
Application bootstrap and WebXR session setup | ECS world creation, RATK integration, ARButton configuration with optional features (local-floor, layers, anchors, unbounded), framebuffer scaling, foveation control | |
Three.js rendering pipeline | multiviewStereo rendering for Quest, passthrough-ready alpha rendering, environment-based lighting | |
Shared state container | Service locator pattern for lightweight ECS architectures | |
Controller lifecycle and head tracking | WebXR controller connected/disconnected events, GamepadWrapper integration, XRViewerPose-based head tracking | |
Pointer visualization and calibration | Attaching objects to controller ray space, per-hand offsets, runtime recalibration | |
Measurement data model and rendering | ECS component/system pattern, spatial anchor creation with RATK, troika-three-text for SDF text rendering, unit conversion (metric/imperial), haptic feedback on distance change, billboard text with lookAt() | |
Single-hand tape measure mode | Trigger-based measurement start/finalize, squeeze-based axis snapping, entity lifecycle management | |
Dual-hand caliper mode | Two-controller coordinate tracking, simultaneous endpoint updates | |
Floating settings panel | Thumbstick navigation with deadzone, gaze-following UI with lerp interpolation, haptic feedback on UI interactions, per-hand control tip texture overlays | |
Clear all measurements | Long-press safety pattern with ring progress indicator, selective entity destruction |
elics Entity-Component-System framework to separate data from logic. The World owns all entities, components, and systems. A single component type, MeasurementComponent, stores marker positions, line geometry, text labels, and material references. Seven systems process logic each frame: PlayerSystem updates controller state, SettingsSystem manages the floating UI, PointerSystem renders controller tips, TapeSystem and ClampSystem create and update measurements, PurgeSystem handles deletion, and MeasurementSystem renders all measurements. For the complete registration order, see src/index.js.immersive-ar session with four optional features: local-floor for floor-level tracking, layers for composition control, anchors for spatial persistence, and unbounded for large-scale experiences. On session start, the renderer disables foveation (sets to 0) for uniform rendering precision across the entire field of view, critical for accurate measurement visualization. The framebuffer scale factor is set to 2 for higher resolution.RATK’s createAnchor() method. All measurement objects attach to this anchor’s Object3D, making them persist relative to the physical world even as you move. The sample initializes the anchor lazily on the first frame after session start, guarded by a _justEnteredXR flag.GamepadWrapper abstracts the WebXR Gamepad API with edge-triggered and level-triggered button helpers. The sample uses getButtonClick() for single actions like placing measurements and getButton() for continuous actions like axis snapping. The get2DInputAngle() method detects thumbstick direction for settings navigation with a 0.7 magnitude deadzone. Each controller’s GamepadWrapper instance requires an update() call every frame to track button state transitions. See src/player.js, src/settings.js, and src/tape.js.sqrt(x*x + z*z)) and compare to the absolute vertical distance. If horizontal exceeds vertical, snap to horizontal by constraining Y to the reference point’s Y. Otherwise, snap to vertical by constraining X and Z. The reference point differs by mode: in Tape mode it is the fixed start marker, while in Clamp mode it is the opposite hand’s pointer position. See src/tape.js and src/clamp.js.gamepad._gamepad.hapticActuators[0]?.pulse(intensity, duration). See src/measurement.js, src/settings.js, and src/purge.js.MeasurementComponent with a points array and render a filled polygon mesh.