Part 7: Anchors (WebXR with Babylon.js)
Tags: webxr, beginners, babylonjs, tutorial
Published: 2024-01-11 08:51:29

👀 Stumbled here on accident? Start with the [first part](https://dev.to/taikonauten/welcome-to-the-exciting-world-of-webxr-and-babylonjs-1nll)!

---

Welcome back to the 7th article in this WebXR/Babylon.js series. This part is about Anchors in WebXR.

ℹ️ Remember - you can always run the code associated with this article and follow along using

`npm start --part=7`

---


![attaching a mesh to an anchor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ewm6fj4nv7hr71r51m2.jpeg)
<figcaption>attaching a mesh to an anchor</figcaption>

---

## What are Anchors?

Anchors are used to create a stable reference point in the physical space, which can be tracked by the device's sensors and cameras. When an anchor is set, the virtual object tied to it appears to be part of the real world, staying in the same place as if it were a physical object.

**✅ Anchoring is crucial for maintaining consistency in the virtual environment as it relates to the real world.**

---

## Adding anchors to the scene

To add an Anchor to the scene, we go back into our `handleControllerSelection`, take the `raycastHit` value and call `addAnchorAtPositionAndRotationAsync` to add an anchor at the ray cast hit position.


## Interacting with the controller

```typescript
handleControllerSelection() {
  ...
    if (raycastHit.pickedMesh === this._box) {
        const mat = this._box!.material as StandardMaterial;
        mat.diffuseColor = Color3.Random();
        this._box!.material = mat;
    } else {

        this.addAnchorAtPosition(raycastHit);
    }
    this._box!.isVisible = true;
  ...
}
```

We first check if the mesh we picked is the box and if so, we just give it a randomly different color.

If we don’t pick the box but any other position in the 3D space we will call a new function `addAnchorAtPosition` with the `raycastHit` as a parameter.


## Adding the Anchor

```typescript
addAnchorAtPosition(raycastHit: PickingInfo) {
    this._xrAnchors!.addAnchorAtPositionAndRotationAsync(raycastHit.pickedPoint!).then((anchor) => {

        const boxTransformNode = new TransformNode('boxTransformNode');
        boxTransformNode.position = raycastHit.pickedPoint!;

        this._box!.parent = boxTransformNode;
        this._box!.position = Vector3.Zero();
        this._box!.isVisible = true;

        anchor.attachedNode = boxTransformNode;
    });
}
```

This is where we add a new anchor at our `raycastHit.pickedPointposition`.

Since anchors have a fixed position in space and don’t support animations, we first have to help ourselves by creating a so called `TransformNode` for the rotating box. A TransformNode is a versatile and flexible helper that, in our case, acts as a helper to the box by acting as parent to it. Therefore the box keeps it’s rotation animation.

The position of this transform node is set to the point where the ray cast hit, making it align with the XR anchor.

The box is then parented to `boxTransformNode`. This means any transformation applied to `boxTransformNode` will also affect the box. The box's position is set to `Vector3.Zero()`, which means it's placed at the origin relative to its parent (i.e., the `boxTransformNode`). Since the transform node is already positioned at the ray cast hit point, this effectively places the box there.

`this._box!.isVisible = true;` makes the box visible in the scene.

Finally, the transform node is attached to the XR anchor. This ensures that the transform node (and therefore the box) will maintain its position relative to the real world, even as the user moves around in the XR space.


## Cleaning up

```typescript
observeAnchors() {
    if (this._xrAnchors === null) {
        return;
    }
    this._xrAnchors.onAnchorAddedObservable.add((addedAnchor) => {
        this._xrAnchors!.anchors.forEach((anchor: IWebXRAnchor) => {
            if (anchor !== addedAnchor) {
                anchor.remove();
            }
        });
    })
}
```

`this._xrAnchors.onAnchorAddedObservable.add(() => { ... })` sets up an observer that will execute the provided callback function every time a new anchor is added to the XR environment.

Within the callback, there's a loop `this._xrAnchors!.anchors.forEach((anchor: IWebXRAnchor) => { ... })` that iterates over all the anchors currently in `this._xrAnchors`.

Inside the loop, `anchor.remove()` is called on each anchor **except the one we just added**. This means that every time a new anchor is added to the system, all existing anchors are removed.

The primary intent of this function is to maintain a single active anchor in the XR environment. When a new anchor is added, it triggers the removal of all existing anchors. This is useful in scenarios where only the latest anchor is relevant, and previous anchors should not persist.

---

## Adding `observeAnchors` to the scene

```typescript
async createScene(): Promise<Scene> {
  ...
  this.observeAnchors();
  
  return this._scene;
}
```

To make use of the function we have to add it to `createScene`.

---

{% vimeo 901446620 %}

---

## Conclusion

In conclusion, this seventh installment of our WebXR/Babylon.js series has taken us through the pivotal concept of Anchors in WebXR. Anchors are more than just a technical component in the realm of extended reality; they serve as the vital bridge connecting the virtual and real worlds. By creating these stable reference points in physical space, which remain consistent regardless of the viewer's movement or perspective, we enhance the immersion and believability of our virtual environments.

The practical demonstrations and code examples provided, from handling controller selections to dynamically adding and managing anchors, have illustrated how these concepts are not just theoretical but highly applicable in real-world scenarios. The use of TransformNode as a flexible tool to maintain object animations while anchoring, and the strategic management of anchors to keep the XR environment clean and relevant, show the depth and versatility of Babylon.js in creating compelling XR experiences.

In the eighth part we dive into loading models and assets.