Skip to content

World Tracking — Three.js

Full integration of Monolook World with Three.js for surface detection and 3D content placement.

Requirements

  • three installed (npm install three)
  • Monolook World license key
  • HTTPS + Chrome on Android, or Monolook App / AppClip on iOS

Setup

js
import * as THREE from 'three'
import { Monolook } from 'monolook/world'

const scene    = new THREE.Scene()
const camera   = new THREE.PerspectiveCamera(70, innerWidth / innerHeight, 0.01, 100)
const renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true })
renderer.setPixelRatio(Math.min(devicePixelRatio, 2))
renderer.setSize(innerWidth, innerHeight)

scene.add(new THREE.HemisphereLight(0xffffff, 0x303030, 2.4))

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(0.35, 0.35, 0.35),
  new THREE.MeshStandardMaterial({ color: 0xf4f4f4 })
)
scene.add(cube)

const monolook = new Monolook({
  adapter: 'three',
  THREE,
  scene,
  camera,
  renderer,
  domOverlayRoot: document.getElementById('dom_overlay'),
})

Events

Register events before calling start():

js
monolook.on('surfacefound', () => {
  // surface detected — show placement indicator to the user
})

monolook.on('select', () => {
  // user tapped — place content at indicator position
  const pose = monolook.getIndicatorPose()
  if (pose?.visible) {
    cube.position.set(pose.position.x, pose.position.y, pose.position.z)
    monolook.pauseTracking()
    monolook.setIndicatorVisible(false)
  }
})

monolook.on('sessionend', () => {
  // AR session closed
})

Start AR

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

monolook.startTracking()

monolook.setRenderLoop(() => {
  monolook.updateTracking()
  renderer.render(scene, camera)
})

await monolook.enterAR()

Reposition and reset

js
// reposition — resume tracking from current position
monolook.startTracking()
monolook.setIndicatorVisible(false)

// full reset after session ends
monolook.reset()

Complete example

js
import * as THREE from 'three'
import { Monolook } from 'monolook/world'

const scene    = new THREE.Scene()
const camera   = new THREE.PerspectiveCamera(70, innerWidth / innerHeight, 0.01, 100)
const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('scene'), alpha: true, antialias: true })
renderer.setPixelRatio(Math.min(devicePixelRatio, 2))
renderer.setSize(innerWidth, innerHeight)

scene.add(new THREE.HemisphereLight(0xffffff, 0x303030, 2.4))

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(0.35, 0.35, 0.35),
  new THREE.MeshStandardMaterial({ color: 0xf4f4f4 })
)
cube.visible = false
scene.add(cube)

const monolook = new Monolook({
  adapter: 'three',
  THREE,
  scene,
  camera,
  renderer,
  domOverlayRoot: document.getElementById('dom_overlay'),
})

let isPlaced = false

monolook.on('surfacefound', () => setStatus('Surface found — tap to place'))
monolook.on('select', placeCube)
monolook.on('sessionend', () => {
  isPlaced = false
  cube.visible = true
  renderer.render(scene, camera)
})

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

  cube.visible = false
  isPlaced = false

  monolook.startTracking()
  monolook.setRenderLoop(() => {
    monolook.updateTracking()
    renderer.render(scene, camera)
  })

  await monolook.enterAR()
}

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

  isPlaced = true
  cube.position.set(pose.position.x, pose.position.y, pose.position.z)
  cube.visible = true
  monolook.pauseTracking()
  monolook.setIndicatorVisible(false)
}

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