World Tracking — Three.js
Full integration of Monolook World with Three.js for surface detection and 3D content placement.
Requirements
threeinstalled (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)