GuidesReferenceIssuesSupportGet API Key

Tracking and Anchors

When you create a ViroViewARCore, the first thing you'll notice is the camera feed rendered as the background to your ARScene. That camera background represents the real world. Viro enables you to fuse virtual objects and UI with that real world. You accomplish this with tracking and anchors.

Camera Tracking

The Viro platform supports development of 6 degrees-of-freedom (6DOF) AR experiences. The platform tracks the user's real-world rotation and position as he or she moves, and keeps the virtual camera in sync with that movement. The platform maintains a right-handed coordinate system, where the origin of the system is the user's location at the time AR tracking was initialized. The scale of the coordinate system is zero. The camera's forward vector (the direction in which it is facing) -- is initially [0, 0, -1], and the camera's initial up vector is [0,1,0].

AR Scenes and Anchors

AR experiences are created in Viro by creating a ViroViewARCore, and configuring and setting an ARScene on that view. ARScene blends virtual 3D content with the device camera's view of the real world. Similar to a normal Scene, an ARScene contains a hierarchy of Node objects representing the virtual 3D world. Behind these objects the camera's live video feed is rendered. ARScene ensures the two worlds -- real and virtual -- stay in sync.

There are two ways to add virtual content to the ARScene, described below.

Manual Anchoring

The first is to place content manually. While it's possible to add a Node to the ARScene at an arbitrary point in world coordinate space, this is not recommended: if AR conditions change, the Node may begin to drift and disconnect from its initial position. Instead, the preferred way to add content to arbitrary positions is to anchor that content using ARScene.createAnchoredNode(Vector). This method returns an ARNode at the provided position. An ARNode is a Node that's affixed to the real-world via an ARAnchor. Each ARNode serves as a regular scene graph node: you can add child nodes, lights, sounds, and more, and this entire virtual subgraph will stay in sync with the real world. ARNodes will stay latched to their position until ARNode.detach() is invoked. Detaching an ARNode will remove it from the ARScene and make it unusable.

The following example shows how to construct an ARNode at an arbitrary position 2 meters in front of the user. A box is added to the ARNode, and "Hello World" text is displayed in a child node above the box.

// Create the AR view and scene
ViroView view = new ViroViewARCore(context, null);
final ARScene scene = new ARScene();

// Create a new anchored node 2 meters in front of the user
ARNode node = scene.createAnchoredNode(new Vector(0, 0, -2));

// AR node creation can fail in poor lighting conditions
if (node != null) {
    Box box = new Box(1, 1, 1);
    // Create a child node for the text, above the box
    Node textNode = new Node();
    textNode.setPosition(0, 1, 0);
    // Create the 'Hello World' text
    Text text = new Text(view.getViroContext(), "Hello World", 1, 1);


Trackable Anchoring

The second way to add virtual content is to listen for features (also called "trackables") detected by the AR system. Trackables are things like vertical or horizontal surfaces, or images (like posters or markers) found in the real world. Each time one of these is found by the AR system, an associated ARAnchor is created. You can associate your content to these features by adding your content to the ARNode that corresponds to each anchor. To do this, implement the ARScene.Listener. The ARScene.Listener callbacks are invoked whenever ARAnchors are found (or updated, or removed).

In the example below, we create an ARScene and set it up to add a virtual Box to sit on every detected horizontal real-world plane.

final ARScene scene = new ARScene();

scene.setListener(new ARScene.Listener() {
    public void onAmbientLightUpdate(float intensity, Vector color) {
    public void onAnchorFound(ARAnchor anchor, ARNode arNode) {
        // Create a Box to sit on every plane we detect
        if (anchor.getType() == ARAnchor.Type.PLANE) {
            ARPlaneAnchor planeAnchor = (ARPlaneAnchor) anchor;
            // Ensure this is a horizontal plane
            if (planeAnchor.getAlignment() == ARPlaneAnchor.Alignment.HORIZONTAL) {
                Box box = new Box(1, 1, 1);
                Node boxNode = new Node();
    public void onAnchorRemoved(ARAnchor anchor, ARNode arNode) { 
    public void onAnchorUpdated(ARAnchor anchor, ARNode arNode) {
    public void onTrackingUpdated(TrackingState state, TrackingStateReason reason) {

ViroView view = new ViroViewARCore(context, null);