webarkit / webarkit/jsfeatNext
[Roadmap] WebAR on jsfeatNext: two-layer architecture & high-level feature set
@kalwalt is already working on this.
Since Jul 25, 2026.
- Dominant language
- TypeScript
- Stars
- 12
- Forks
- 4
- Avg merge
- 16h 24m
- Merged PRs (30d)
- 34
Description
Umbrella / roadmap issue. Captures the architecture and the full feature set for building a WebAR solution on top of jsfeatNext, from the "Libreria realtà aumentata con jsfeatNext" design discussion. Individual components will spin off into their own issues (in a dedicated high-level AR repo once it exists).
Goal
Natural-feature-tracking WebAR for planar (image) targets — the classic ARToolKit-NFT / Vuforia-image-target use case: detect a reference image in the camera feed and anchor a 3D object on it. (Not full SLAM, not arbitrary surfaces.)
Two-layer architecture
The decision from the discussion: jsfeatNext stays a generic, stateless image-processing / CV library. Everything stateful and AR-specific lives in a separate high-level, stateful AR project (TypeScript), which talks to the CV backend only through the minimum CvBackend interface (#96).
┌─────────────────────────────────────────────────────────────┐
│ High-level AR project — TypeScript, STATEFUL │
│ target training · tracking loop · pose refinement · │
│ geometric validation · temporal filter · renderer adapters │
└───────────────────────────┬─────────────────────────────────┘
│ CvBackend interface (#96)
│ detect · describe · match ·
│ estimateHomography · poseFromHomography
┌───────────────┬───────────────┬──────────────┬──────────────┐
standard WASM WebARKitLib jsartoolkitNFT
(jsfeatNext) (PureCV) (OpenCV/WASM) (KPM/WASM)
pure TS/JS Rust→WASM ← already ← future candidate
portable fast implements (needs rewrite)
this pipeline
The layer is backend-agnostic and "elastic": pick standard (jsfeatNext), WASM (PureCV), or — with the rewrite noted in #96 — WebARKitLib / jsartoolkitNFT, all behind the same contract.
Readiness map (state of the code)
✅ In jsfeatNext (stateless CV primitives) — the vision half is essentially done:
- Grayscale + image pyramid (
imgproc,pyramid_t) - Keypoint detection (
fast_corners,yape,yape06) - ORB descriptors (
orb.describe) - Homography + RANSAC (
homography2d,motion_estimator) - Optical flow (
optical_flow_lk) — the building block for frame-to-frame tracking
🟡 Prototyped (promote to jsfeatNext modules) — tracked in #83:
bfmatcher— brute-force Hamming matcher (was inline insample_orb_pinball.html)pose_estimator—H → (R, t)planar pose decomposition (camera frame only)
⭐ Prior art: WebARKitLib already implements this pipeline (OpenCV/WASM)
Important context — the high-level layer is NOT green-field. WebARKitLib (C++ → emscripten/WASM, driven by webarkit-testing, an emscripten port of artoolkitX's OCVT planar-tracking design) is a working implementation of exactly this pipeline, built on OpenCV:
- Detection/description:
cv::ORBandcv::AKAZE - Matching:
cv::BFMatcher - Tracking loop:
cv::calcOpticalFlowPyrLK+TrackingPointSelector— i.e. thetracker_tdetect↔track↔recover machinery already exists there - Homography:
WebARKitHomographyInfo - Renderer adapters:
getPoseMatrixGL()andgetCameraProjectionMatrix()— themodelViewGL/projectionGLequivalents, already solved - JS side:
webarkit-testing'sWebARKitController.js(init_raw()/process_raw(),getMarkerevent, pose/matrix getters) is a real high-level controller
What this means for this roadmap: every component below should be designed with WebARKitLib as the reference, not invented from scratch — it is the proven design (and a numeric/behavioural comparison target). The open question is not how to build a planar tracker, but how to factor it so the same orchestration works over a pluggable backend (#96) instead of being fused to one CV implementation.
It also plugs into #96 in two distinct ways:
- As a granular
CvBackend— needs the "valuable rewrite" to expose primitives individually (its public API isprocessFrameData()→ pose, a bundled pipeline). - As a whole-pipeline tracker — it could satisfy a coarser
TrackerBackendseam (frame in → pose out) as-is, with no rewrite, letting the high-level layer delegate wholesale instead of composing primitives. Worth evaluating before writingtracker_tfrom zero.
🔴 To build — the high-level AR layer (this roadmap):
- Target training —
pattern_t. Reference image (+ its real dimensions) → reusable multi-scale descriptor set with associated model-plane coordinates. Today this is throwaway inline code in the sample; needed to make "targets" first-class objects. - Tracking loop —
tracker_t⭐ the real bottleneck. Adetect → track → recoverstate machine: once the target is locked, don't re-run ORB every frame — follow the inlier points frame-to-frame withoptical_flow_lkand re-estimate the homography from the tracked points; only re-run the heavy detection when tracking is lost or confidence drops. This is what turns a stuttering demo into a 30–60 fps tracker (likely why the sample "didn't work well"). All the bricks exist (optical_flow_lk,pyramid_t,motion_estimator) — the orchestration is what's missing. - Renderer adapters —
modelViewGL+projectionGL.modelViewGL(pose): column-major 4×4 for three.js/WebGL, CV→GL viadiag(1,-1,-1).projectionGL(K, w, h, near, far): the GL projection consistent with the same intrinsicsK— without it, even a perfect pose won't line up. These are rendering glue, so they live here, not in jsfeatNext (keeps the CV lib renderer-agnostic). (Moved out of jsfeatNext scope — see the #83 correction.) - Geometric validation. Reject implausible RANSAC homographies (projected-corner convexity, determinant sign, aspect-ratio sanity) to stop jitter/flipping.
- Pose refinement. Non-linear minimization of reprojection error (small Gauss-Newton / Levenberg-Marquardt);
linalggives the algebra, the solver is missing. - Temporal filter. Smooth the pose across frames to kill jitter.
- Undistort (accuracy). Apply known distortion coefficients (could be an
imgprochelper in jsfeatNext, or high-level). Full calibration is done offline;pose_estimator.intrinsics()bootstraps without it.
⚪ App layer (out of the library entirely): camera capture (getUserMedia → canvas → RGBA), the engine loop stitching capture → CV → pose → rendering, and the three.js bridge. Running the CV pipeline in a WebWorker keeps the main thread free — and heavy frames are exactly where the WASM (PureCV) backend beats pure JS.
Open question worth settling early
Given WebARKitLib already works: is the goal (a) a new TS orchestration layer that can drive any backend — with jsfeatNext as the portable no-WASM option and WebARKitLib/PureCV as the fast ones — or (b) jsfeatNext-based AR specifically, as a dependency-free alternative to the WASM stack? The components below are the same either way, but it changes whether tracker_t is written fresh or wraps an existing tracker.
Suggested sequencing
pattern_t(target training) +projectionGL— closes the end-to-end loop so you can see something anchored.tracker_t— the detect↔track↔recover loop; the single biggest lever from "demo" to "usable".- Then robustness: geometric validation → temporal filter → pose refinement.
What stays in jsfeatNext vs here
| In jsfeatNext (stateless, generic) | High-level AR project (stateful) |
|---|---|
bfmatcher, pose (R,t), detect/ORB, estimateHomography (#83, #96) |
pattern_t, tracker_t, pose refinement, geometric validation, temporal filter, renderer adapters (modelViewGL, projectionGL) |
Non-goals
- Full SLAM / arbitrary-surface tracking (planar image targets only).
- Real camera calibration workflow (done offline; only bootstrap intrinsics here).
- Creating the high-level AR repo in this issue — this is the roadmap; the repo + per-component issues come when the work starts.
Related
- CV backend contract: #96
- CV primitives to promote: #83
- Prototypes & full design rationale: the "Libreria realtà aumentata con jsfeatNext" discussion.
- Prior art / reference implementation: WebARKitLib (OpenCV → WASM tracker) and webarkit-testing (build glue, JS controller, examples).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.