Skip to content

World Tracking — A-Frame

Integration of Monolook World with A-Frame for surface detection and 3D content placement.

Requirements

  • A-Frame loaded via CDN (no npm install needed)
  • Monolook World license key
  • HTTPS + Chrome on Android, or Monolook App / AppClip on iOS

HTML setup

html
<!doctype html>
<html>
<head>
  <script src="https://aframe.io/releases/1.6.0/aframe.min.js"></script>
  <style>
    .a-enter-vr, .a-enter-ar { display: none !important; }
  </style>
</head>
<body>
  <div id="dom_overlay">
    <!-- your UI here -->
  </div>

  <a-scene id="aframeScene" embedded
    vr-mode-ui="enabled: false"
    renderer="alpha: true; antialias: true">
    <a-entity camera position="0 1.6 0"></a-entity>
    <a-box id="cube" color="#f4f4f4" width="0.35" height="0.35" depth="0.35"
      position="0 0 -1.2"></a-box>
  </a-scene>

  <script type="module" src="main.js"></script>
</body>
</html>

Hide the default A-Frame VR/AR buttons — Monolook handles the session.

Setup

Wait for the A-Frame scene to finish loading before creating the adapter:

js
import { Monolook } from 'monolook/world'

const sceneEl = document.getElementById('aframeScene')
const cubeEl  = document.getElementById('cube')

// wait for A-Frame scene before creating Monolook
if (!sceneEl.hasLoaded) {
  await new Promise(r => sceneEl.addEventListener('loaded', r, { once: true }))
}

const monolook = new Monolook({
  adapter: 'aframe',
  sceneEl,
  domOverlayRoot: document.getElementById('dom_overlay'),
})

Events and start

js
monolook.on('surfacefound', () => setStatus('Surface found — tap to place'))
monolook.on('select', placeCube)
monolook.on('sessionend', resetAfterSession)

await monolook.start({ mode: 'surface', licenseKey: '<WORLD_LICENSE_KEY>' })

monolook.startTracking()
monolook.setRenderLoop(() => monolook.updateTracking())
await monolook.enterAR()

Placement

A-Frame entities accept position as an object directly:

js
function placeCube() {
  if (!monolook.isPresenting() || isPlaced) return
  const pose = monolook.getIndicatorPose()
  if (!pose?.visible) return

  isPlaced = true
  cubeEl.setAttribute('position', pose.position)
  cubeEl.setAttribute('visible', true)
  monolook.pauseTracking()
  monolook.setIndicatorVisible(false)
}

Complete example

js
import { Monolook } from 'monolook/world'

const sceneEl = document.getElementById('aframeScene')
const cubeEl  = document.getElementById('cube')

let monolook
let isPlaced = false

async function init() {
  if (!sceneEl.hasLoaded) {
    await new Promise(r => sceneEl.addEventListener('loaded', r, { once: true }))
  }

  monolook = new Monolook({
    adapter: 'aframe',
    sceneEl,
    domOverlayRoot: document.getElementById('dom_overlay'),
  })

  monolook.on('surfacefound', () => !isPlaced && setStatus('Surface found — tap to place'))
  monolook.on('select', placeCube)
  monolook.on('sessionend', () => {
    isPlaced = false
    cubeEl.setAttribute('visible', true)
  })

  window.addEventListener('pointerup', placeCube)
  document.getElementById('startButton').addEventListener('click', startAR)
}

async function startAR() {
  await monolook.start({ mode: 'surface', licenseKey: '<WORLD_LICENSE_KEY>' })
  isPlaced = false
  cubeEl.setAttribute('visible', false)
  monolook.startTracking()
  monolook.setRenderLoop(() => monolook.updateTracking())
  await monolook.enterAR()
}

function placeCube() {
  if (!monolook.isPresenting() || isPlaced) return
  const pose = monolook.getIndicatorPose()
  if (!pose?.visible) return

  isPlaced = true
  cubeEl.setAttribute('position', pose.position)
  cubeEl.setAttribute('visible', true)
  monolook.pauseTracking()
  monolook.setIndicatorVisible(false)
}

init()