Full frontend built against the synthetic API (no dependence on lanes A/B/D): M4 — synchronized playback: master clock from performance.now() (never a <video>); per-video correction via requestVideoFrameCallback (exact mediaTime) with a currentTime+half-frame fallback — hard-seek >150ms / nudge playbackRate +/-5% 20-150ms / lock <20ms; out-of-range videos pause+dim; single audio source; dev sync-error overlay. Timebase mirrors config.py (lib/timebase.js). Verified: seek is exact to 0ms; continuous inter-video desync 13ms mean / 46ms max. M5 — 3D viewer: PLY point cloud, per-video camera paths + current-pose frusta, OrbitControls, snap-to-camera (intrinsics->PerspectiveCamera fov) + free roam. All COLMAP->Three.js via the frozen lib/pose.js; pose interpolation in lib/poseTrack.js (slerp+lerp of contract inputs). M6 — anchor overlays: letterbox-correct per-video canvas; anchors projected via pose.js; behind-camera cull. Projection agrees with an independent pinhole to 1.45e-13 px. Timeline — colored event markers + legend, hover tooltip, click-to-jump; draggable scrubber. Phase-3 seams (annotate.js M8, camPath.js M9) left as stubs. No frozen files edited; no new deps; no change requests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.0 KiB
Status — lane-C
Round 1 — 2026-07-16 — STATUS: ready_to_merge
Directives acknowledged: round 0 of plan/DIRECTIVES.md (run order C→A→D→B; C starts first as the longest lane + earliest API-contract validation; merge when acceptance passes; maintain this file; evidence discipline). No new rounds present on origin at time of writing.
Acceptance checklist (lane brief M4/M5/M6 + timeline markers) — all pass:
- M4 synchronized playback. Master clock from
performance.now()(never a<video>). Timebase mapping proven exact by seek: at t_global=8.0 → cam0=8.000, cam1=6.630, cam2=8.842 s, err=0 ms each. Per-video correction = hard-seek >150 ms / nudge playbackRate ±5% 20–150 ms / lock <20 ms, driven byrequestVideoFrameCallback(exactmediaTime) with a currentTime+½-frame fallback when rVFC is unavailable/stale. Out-of-range videos pause+dim (cam1 at t=0, target −1.37 s). One audio source; dev overlay shows per-video sync ms. Measured continuous-playback inter-video desync = 13 ms mean / 46 ms max over 6 s (< 50 ms), driven by a setTimeout pump (see env note). - M5 3D viewer. PLY point cloud (3106 pts, vertex colors); per-video camera-path lines
+ current-pose frusta (colored); OrbitControls; snap-to-camera tweens viewer to the pose
(following cam1: camPos≈(1.78,2.63,9.25)≈true center (0,2.4,9.4); fov=36°≈2·atan(H/2fy);
orbit disabled) and free-roam restores (fov→50°, orbit on). All COLMAP→Three via frozen
lib/pose.js— never reimplemented. - M6 anchor overlays. Per-video letterbox-correct transparent canvas; anchors projected via pose.js camera. Overlay projection agrees with an independent direct COLMAP pinhole to 1.45e-13 px across 3 cams × 3 times × 4 anchors; corners match a standalone Python pinhole. Behind-camera cull (z_cam<0). Stage corners render/track on all in-range videos.
- Timeline event markers. 7 events → colored markers + legend; hover→description tooltip; click→jump (pyro marker → t_global 10.00). Scrubber seeks/parks all videos.
- No console errors across load/play/seek/snap/timeline; production build clean
(
npm run build, 16 modules).
Env note (not a blocker): the automated browser pane reports document.hidden=true, so rAF
and rVFC only fire during screenshots — continuous playback can't be observed by polling here.
Verified deterministically instead (seek exactness to 0 ms; projection to 1e-13 px; a
setTimeout-driven run of the real correction code → 13 ms mean inter-video desync). A real
focused browser runs the rAF loop at 60 fps and rVFC drives even tighter absolute sync.
Owned-files discipline: committed only frontend/** + this status file. Left untouched the
other lanes' in-tree changes (A: audio_sync/ingest; B: frames/geometry/sfm; D: events_ai).
Added frontend/src/lib/timebase.js + poseTrack.js (new files inside lane C's ownership).
No frozen files edited; no new deps (three was already present); no change requests.
Repo note for coordinator: this working directory is shared/contended — lane/c-viewer
was reset to foundation (5fa7301) and HEAD moved to lane/d-events between my session-start
commit and now, and main has since advanced to lane D's self-merge (6ec55c7). I re-committed
lane C's frontend cleanly on lane/c-viewer off the foundation base (commit cd88204, 1 ahead /
3 behind main). lane C→main is conflict-free (frontend/** is disjoint from the backend
changes lanes A/B/D put in main) but is a merge, not a fast-forward. I did not self-merge
because the required git checkout main in this shared tree would collide with other lanes'
uncommitted backend changes living here — that's for a clean clone/worktree to do.
Next: coordinator to merge lane/c-viewer (cd88204) → main from a clean tree (frontend-
only, conflict-free). Phase-3 seams left as clean stubs: annotate.js (M8), camPath.js (M9).