Pocket Tape AR is a browser-based, two-point distance-measurement tool that runs on any WebXR-capable mobile browser. Users can place endpoints with classic WebXR plane hit-tests or the Triangulation (beta) flow that uses two taps per point from different poses. After solving the 3-D points the app draws a coloured line, displays the distance with an accuracy estimate (±), and adds the entry to a session list. It targets DIY hobbyists and professionals with a high-precision workflow while staying lightweight and build-free.
- Launch
- AR scene: WebXR session starts in portrait mode (or “AR not supported” overlay).
- HUD / 2-D UI: Top-center banner empty. FAB opens the measurement list from the bottom-left.
- Notes: Camera permission requested only if not yet granted.
- Toggle modes
- AR scene: Menu button (☰) anchored top-left.
- HUD / 2-D UI: Menu sheet lists Triangulation (beta) switch with tooltip text.
- Notes: Default is classic hit-test mode.
- Place 1st point
- AR scene: White sphere marker appears.
- HUD / 2-D UI: Banner shows live distance = 0.00 m / 0.00 ft.
- Notes: Haptic tick.
- Aim
- AR scene: Reticle follows hit-test; live provisional line connects marker → reticle.
- HUD / 2-D UI: Banner updates every frame (fixed precision).
- Notes: Banner only—no in-scene label yet. If the reticle stays unavailable for ~1.2 s, a bottom prompt suggests switching to Triangulation.
- Place 2nd point
- AR scene: Second sphere marker, coloured line, and mid-point label billboard (CSS2D text).
- HUD / 2-D UI: Banner freezes to final value, then clears on next measurement.
- Notes: Haptic tick.
- Session list
- AR scene: No additional in-scene elements.
- HUD / 2-D UI: Tap FAB → modal sheet slides up. Each row: coloured dot, value + units, copy ❐, delete ✖, unit-toggle switch (Imperial/Metric) at top.
- Notes: Modal closes when done; unit preference persists between sessions.
- Enable via ☰ → toggle Triangulation (beta). Crosshair replaces plane reticle and a hint banner appears (“Triangulation on — each point needs two taps.”)
- Tap #1 with the crosshair centred. HUD shows “Move sideways ~30–40 cm…” plus a
1/2badge and baseline progress ring (goal 0.35 m). - Baseline guidance runs until the device moves ≥ 0.30 m (hard minimum) between taps. If the user stalls for 10 s a tip is shown.
- Tap #2 re-aims the same physical point. The app triangulates the 3-D point, then displays a quality chip:
- Green: angle ≥ 8° and ray miss ≤ 1.5 cm (chip auto-hides after ~1.6 s, manual Accept still required).
- Yellow: angle 4–8° or miss 1.5–3 cm (prompt to widen baseline, Accept allowed).
- Red: angle < 4° or miss > 3 cm (banner urges Redo, Accept anyway enabled).
- Quick actions: bottom-left
Undo tap #1, bottom-rightRedo #2&Accept, top-right✕cancels current endpoint. - Complete measurement: after both endpoints resolve, the coloured line is drawn, the midpoint label shows
distance (± miss)when available, and the entry is added to Measurements with the same error bar.
All stored data (ray origins/directions, quality score, error estimate) is kept per endpoint to support redo/undo without impacting existing measurements.
| Area | Requirement |
|---|---|
| Measurement | • Only straight-line, two-point measurements. • Unlimited concurrent lines. • Internal unit = metres (Float64). |
| Triangulation | • Optional mode toggled via ☰ menu. • Each endpoint captured by two taps from distinct poses. • Enforce ≥ 0.30 m baseline before accepting second tap. • Store both rays/poses to support redo/undo per endpoint. |
| Precision & Format | • Metric: metres to 2 decimals (≥ 1 m) or centimetres (cm) under 1 m. • Imperial: feet + decimal inches (e.g., 4 ft 7.25 in). |
| Error Reporting | • Display adds ± only when error > 0 (triangulation); classic hit-test entries omit error text.• Error derived from ray–ray miss distance for triangulation. • Clipboard copy includes the same formatted string. |
| Units | • Conversion occurs at display layer only. • Default system guessed from navigator.language, preference persisted in localStorage.• User can toggle in list modal. |
| Clipboard | • Copies the full formatted string (e.g., 2.54 m (±0.8 cm)). |
| Session List | • Always starts empty on reload. • Rows show coloured dot + formatted string with copy/delete actions. • Delete uses the browser confirm() prompt before removal from list and scene. |
| Clear-all | • No bulk clear; users reload page or delete items individually. |
| Editing | • Measurements are read-only once placed. |
| Colouring | • Each new measurement advances hue by 137° for contrast; midpoint label & list dot share colour. |
| Feedback | • Haptic tick on each placement; crosshair appears only in triangulation; no audio. • After ~1.2 s without a hit-test reticle, show a bottom prompt offering to switch to Triangulation. |
| Tracking Loss | • Hit-test mode shows yellow banner: “Move phone to regain tracking.” • UI remains interactive; placement disabled until tracking resumes. |
| Unsupported Browser | • Full-screen overlay with message + device suggestions; offers optional “View 3D Scene (No AR)” fallback when WebGL is available. |
- Performance: Target ≥ 30 fps on mid-range devices with 20+ lines. Use simple Three.js materials (unlit line + spheres).
- Orientation: Attempt to lock portrait via
screen.orientation.lock('portrait'); ignore failures. - Accessibility: Maintain WCAG-AA contrast for banners and modals; no additional colour-blind accommodations in v1.
- Privacy: Avoid analytics, cookies, or external tracking; only CDN modules are requested.
- Security: Host over HTTPS; no service worker or offline cache in the current build.
- Supported Platforms: Require
navigator.xr.isSessionSupported('immersive-ar')to return true (Chrome ≥ 94 Android, iOS Safari ≥ 15, Samsung Internet, Vision Pro Safari). Otherwise show the unsupported overlay with fallback option.
- 3-D / AR:
src/app.jsimports Three.js 0.165 (from unpkg) plusARButton,CSS2DRenderer, and WebXR APIs. - UI: Vanilla JS + Tailwind CDN; DOM IDs defined in
index.htmland wired inapp.js. - Modules: No bundler; native ES modules with an
importmapinindex.html. A lightweight support widget is injected via@csutil/support-bubble. - Caching: No manifest or service worker; the app runs entirely from the static HTML + modules.
- Hit-testing: Request viewer reference + hit-test source; show classic reticle only when a hit result is available.
- Distance calculation: Compute
distance = p1.distanceTo(p2)in metres. Metric display uses metres with two decimals when ≥ 1 m, otherwise centimetres rounded to integers. Imperial display converts to inches (metres × 39.3701), splits into feet (Math.floor(totalInches / 12)) and decimal inches ((totalInches % 12).toFixed(2)). - Colour generator: Fix HSL saturation at 80% and lightness at 55%, advancing hue with
nextHue = (prevHue + 137) % 360to guarantee contrast. - Triangulation: Compute camera forward ray on tap, enforce ≥ 0.30 m device baseline before solving closest-point-of-approach between rays. Result classification: Green (θ ≥ 8° & miss ≤ 1.5 cm), Yellow (θ ≥ 4° or miss ≤ 3 cm), Red otherwise. Error bar combines endpoint misses via RSS.
- Camera permission denied / session init fails: Show unsupported overlay and keep Start button hidden.
- WebXR support missing: Offer unsupported overlay with optional “View 3D Scene” fallback.
- Unhandled JS error: Log details to the console only; no telemetry.
- CDN hiccups: Rely on network fetch; no offline caching in current build.
- Unit tests: Jest suite covering measurement formatting helpers and triangulation feedback UI helpers (
src/formatters.test.js,src/triangulation-ui.test.js). - Manual exploratory: Checklist covering placement flows, baseline guidance ring, quality chip states, redo/undo, list copy/delete, unit toggle persistence, hit-test tracking banner, and fallback mode.
- CI: Execute GitHub Actions workflow
npm cithennpm test; treat failures as merge blockers.
- Build: No build step; serve
index.html+src/app.jsdirectly (seenpm run serve:static). - Deployment: Publish static assets (index.html + src) to hosting of choice (e.g., GitHub Pages root).
- Versioning: Tag releases as
vMAJOR.MINOR.PATCHper repo conventions. - License: Repository currently includes
LICENSE(GPL v3).
index.htmlwith import map, Tailwind CDN hook, and DOM scaffolding.src/app.js,src/triangulation.js,src/triangulation-ui.js,src/formatters.jsplus colocated Jest specs.- README with quick-start, supported devices, and GPL notice.
- Optional marketing assets (icons) tracked separately.
- Multi-language UI via i18next.
- Per-measurement edit mode.
- High-contrast / colour-blind safe palette toggle.
- Advanced calibration workflow.
- Automated end-to-end Playwright suite + device farm.