How to Parent 3D Model to Three.js AR Marker

To successfully display a dynamically loaded 3D model on a tracking marker in a WebXR or Three.js augmented reality application, you must load the asset asynchronously and attach it directly to the anchor group associated with your tracking marker. This article provides a concise, step-by-step guide and code implementation for loading a GLTF model and properly parenting it to an AR marker so that it tracks accurately in physical space.

1. Create the Marker Group

In Three.js AR frameworks (such as AR.js or MindAR), tracking markers are represented as THREE.Group objects. The tracking library automatically updates the position and rotation of this group based on the camera’s view of the physical marker.

First, initialize your scene, camera, renderer, and the marker group:

import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

// Setup basic Three.js scene
const scene = new THREE.Scene();

// Create a group that will act as the marker anchor
const markerGroup = new THREE.Group();
scene.add(markerGroup);

// Note: Your AR tracking library (e.g., AR.js) must be configured 
// to update this markerGroup's matrix.

2. Load the 3D Model Dynamically

Use the GLTFLoader to load your 3D model. Because loading is asynchronous, you must handle the parenting inside the loader’s success callback function.

const loader = new GLTFLoader();

loader.load(
    'path/to/your/model.gltf',
    (gltf) => {
        const model = gltf.scene;
        
        // Parent the model to the marker group
        parentModelToMarker(model, markerGroup);
    },
    (xhr) => {
        console.log((xhr.loaded / xhr.total * 100) + '% loaded');
    },
    (error) => {
        console.error('An error happened while loading the model', error);
    }
);

3. Position and Parent the Model

When you add the model as a child of the markerGroup, it inherits the marker’s local coordinate system. You must reset or adjust the model’s local position, rotation, and scale to ensure it sits correctly on top of the physical marker.

function parentModelToMarker(model, marker) {
    // 1. Clear any previous models attached to the marker
    while (marker.children.length > 0) {
        marker.remove(marker.children[0]);
    }

    // 2. Adjust local scale to fit the physical marker
    // (AR coordinates are typically in meters)
    model.scale.set(0.5, 0.5, 0.5); 

    // 3. Align the model to the center of the marker
    model.position.set(0, 0, 0);

    // 4. Rotate the model if necessary (e.g., to stand upright)
    model.rotation.set(0, 0, 0);

    // 5. Parent the model to the marker group
    marker.add(model);
}

Key Considerations for AR Alignment