Skip to content

AR-js-org/arjs-plugin-threejs

Repository files navigation

arjs-plugin-threejs ✨🧩

GitHub Stars GitHub Forks CI Status License Open Issues Three.js Version

πŸ§ͺ A Three.js renderer plugin for AR.js-core: mounts a WebGL canvas, consumes AR marker + camera events, and exposes per‑marker Three.js Group anchors for you to attach content.
πŸ”§ Defaults replicate classic AR.js axis handling.
πŸš€ Designed for extensibility, testability (renderer injection), and modern ESM builds.


Table of Contents πŸ“š

Features 🌟

  • βœ… Unified handling for ar:marker, raw ar:getMarker, legacy ar:markerFound / Updated / Lost
  • πŸ”„ Automatic AR.js classic axis transform chain (R_y(Ο€) * R_z(Ο€) * modelViewMatrix * R_x(Ο€/2))
  • 🧬 Optional experimental path (invertModelView, applyAxisFix)
  • πŸͺ Lazy anchor creation (create Three.js Group only when a marker first appears)
  • πŸŽ› Debug helpers: scene & per‑anchor AxesHelper
  • πŸ§ͺ Test-friendly: inject your own renderer via rendererFactory
  • πŸƒ Dual render triggers: engine:update or requestAnimationFrame fallback
  • πŸ›‘ Confidence filtering on marker events
  • 🧹 Clean disable/dispose lifecycle

Install / Build πŸ› 

npm run build:vite

Outputs:

  • ESM: dist/arjs-plugin-threejs.mjs
  • CJS: dist/arjs-plugin-threejs.js
  • Source maps included

Serve the example (choose one):

# If example has its own dev scripts
cd examples/minimal
npm i
npm run dev

# OR from repo root (so relative dist path works)
npx http-server .
# Open: http://localhost:8080/examples/minimal/

Quick start (Engine + Artoolkit + Three.js plugin) πŸš€

import { Engine, webcamPlugin, defaultProfilePlugin } from "ar.js-core";
import { ThreeJSRendererPlugin } from "@AR-js-org/arjs-plugin-threejs";

// 1) Engine & core plugins
const engine = new Engine();
engine.pluginManager.register(defaultProfilePlugin.id, defaultProfilePlugin);
engine.pluginManager.register(webcamPlugin.id, webcamPlugin);
const ctx = engine.getContext();
await engine.pluginManager.enable(defaultProfilePlugin.id, ctx);
await engine.pluginManager.enable(webcamPlugin.id, ctx);

// 2) Artoolkit plugin
const { ArtoolkitPlugin } = await import(
  "./vendor/arjs-plugin-artoolkit/arjs-plugin-artoolkit.esm.js"
);
const artoolkit = new ArtoolkitPlugin({
  cameraParametersUrl: "/path/to/camera_para.dat",
  minConfidence: 0.6,
});
await artoolkit.init(ctx);
await artoolkit.enable();

// 3) Projection
const proj = artoolkit.getProjectionMatrix?.();
const arr = proj?.toArray ? proj.toArray() : proj;
if (Array.isArray(arr) && arr.length === 16) {
  engine.eventBus.emit("ar:camera", { projectionMatrix: arr });
}

// 4) Three.js plugin
const threePlugin = new ThreeJSRendererPlugin({
  container: document.getElementById("viewport"),
  useLegacyAxisChain: true,
  changeMatrixMode: "modelViewMatrix",
  preferRAF: true,
  // debugSceneAxes: true,
  // debugAnchorAxes: true,
});
await threePlugin.init(engine);
await threePlugin.enable();

// 5) Start engine loop
engine.start();

Events handled πŸ””

Event Payload Purpose
ar:marker { id, matrix?, visible? } Unified high-level marker pose/visibility
ar:getMarker { matrix, marker: {...} } Raw worker-level pose (plugin extracts ID/confidence)
ar:markerFound / Updated / Lost legacy shapes Adapted internally to ar:marker
ar:camera { projectionMatrix } Sets camera projection
engine:update any Optional frame trigger (in addition to RAF)

Options βš™οΈ

Option Type Default Description
container HTMLElement document.body Mount target for canvas
preferRAF boolean true Render each RAF even w/o engine:update
minConfidence number 0 Ignore ar:getMarker below confidence
useLegacyAxisChain boolean true Use classic AR.js transform chain
changeMatrixMode string modelViewMatrix Or cameraTransformMatrix (inverts)
invertModelView boolean false Experimental (disabled if legacy chain on)
applyAxisFix boolean false Experimental axis correction (Y/Z Ο€)
debugSceneAxes boolean false Show AxesHelper at scene origin
sceneAxesSize number 2 Size for scene axes helper
debugAnchorAxes boolean false Add AxesHelper per anchor
anchorAxesSize number 0.5 Size for anchor axes helper
rendererFactory function null Inject custom renderer (testing)

Classic AR.js chain:

finalMatrix = R_y(Ο€) * R_z(Ο€) * modelViewMatrix * R_x(Ο€/2)

If changeMatrixMode === 'cameraTransformMatrix', invert at end.

Camera Projection 🎯

const proj = artoolkit.getProjectionMatrix();
const arr = proj?.toArray ? proj.toArray() : proj;
if (Array.isArray(arr) && arr.length === 16) {
  engine.eventBus.emit("ar:camera", { projectionMatrix: arr });
}

Look for log: Projection applied.

Anchors and how to add content 🧱

Anchors are created lazily from the first pose event.

engine.eventBus.on("ar:getMarker", (d) => {
  const id = String(
    d?.marker?.markerId ??
      d?.marker?.id ??
      d?.marker?.pattHandle ??
      d?.marker?.uid ??
      d?.marker?.index ??
      "0",
  );

  // Add content once anchor exists
  setTimeout(() => {
    const anchor = threePlugin.getAnchor(id);
    if (anchor && !anchor.userData._content) {
      anchor.userData._content = true;
      const cube = new THREE.Mesh(
        new THREE.BoxGeometry(0.5, 0.5, 0.5),
        new THREE.MeshBasicMaterial({ color: 0xff00ff }),
      );
      cube.position.y = 0.25;
      anchor.add(cube);
    }
  }, 0);

  // Bridge raw to unified
  if (Array.isArray(d?.matrix) && d.matrix.length === 16) {
    engine.eventBus.emit("ar:marker", { id, matrix: d.matrix, visible: true });
  }
});

Testing πŸ§ͺ

Run tests:

npm test

Watch:

npm run test:watch

Coverage includes:

  • Axis chain vs experimental path
  • Inversion & axis fix effects
  • Confidence filtering
  • Anchor lifecycle (create, reuse, visibility)
  • RAF fallback vs engine:update
  • Projection & inverse
  • Disable/Dispose cleanup
  • Debug helpers presence
  • Matrix invariants (matrixAutoUpdate=false)

Test renderer injection example:

const fakeRenderer = {
  domElement: document.createElement("canvas"),
  setPixelRatio() {},
  setClearColor() {},
  setSize() {},
  render() {},
  dispose() {},
};
const plugin = new ThreeJSRendererPlugin({
  rendererFactory: () => fakeRenderer,
});

CI πŸ€–

GitHub Actions workflow (.github/workflows/ci.yml) runs:

  • Install
  • Build
  • Tests (Node version defined in .nvmrc file) Badge above shows current status.

Type Definitions πŸ”€πŸ§Ύ

TypeScript declaration files are included in the types folder. Prefer importing types from the provided declarations:

  • types/index.d.ts
  • types/threejs-renderer-plugin.d.ts

Source maps (.d.ts.map) are included for better editor/IDE support.

Compatibility πŸ”„

  • Built & tested with Three.js 0.161.x
  • Requires AR.js-core engine abstraction with an event bus (on/off/emit)
  • Should work with any tracking plugin that can emit marker IDs + 4x4 pose matrices

Roadmap Ideas 🧭

  • πŸ”Œ Additional renderer plugins (Babylon / PlayCanvas)
  • 🧷 Multi-marker composition helpers
  • πŸŒ€ Pose smoothing module (optional add-on)
  • πŸ’‘ Example gallery with animated models & GLTF loader integration
  • πŸ§ͺ Visual regression tests (screenshot-based) in CI

License πŸ“„

MIT Β© AR.js Org


Made with ❀️ for Web AR. Contributions welcome! Open an issue / PR πŸ› 

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published