Develop
Develop
Select your platform

Focus showcase - User interaction and positioning

Updated: Sep 29, 2025

Spawn objects relative to user’s position

Ensuring that new objects spawn within the user’s field of view is crucial. The visibility confirms to the user that the object has been created.
To ensure new elements appear in front of a user, their location and viewing direction must be considered.
Steps to use:
  1. Get the user’s position using the getHeadPose() function.
  2. Determine the distance from the user where you want the object to be shown.
  3. Calculate the new position using Vector3.Forward and modify the height to match the user’s head height.
  4. Ensure the spatial object faces the user by calculating the lookRotation and rotating it 180 degrees on the Y axis.
  5. Use the placeInFront() function to place the object in front of the user, incorporating an offset vector for positioning elements in specific poses relative to the user.
// Utils.kt

fun getHeadPose(): Pose {
    val head =
        Query.where { has(AvatarAttachment.id) }
            .eval()
            .filter { it.isLocal() && it.getComponent<AvatarAttachment>().type == "head"}
            .first()
    return head.getComponent<Transform>().transform
}

fun placeInFront(entity: Entity?, offset: Vector3 = Vector3(0f), bigPanel:Boolean = false, nonPanel:Boolean = false) {
    val headPose:Pose = getHeadPose();

    val isToolbar = entity!! == ImmersiveActivity.instance.get()?.toolbarPanel

    // We treat toolbar and big panels differently from other objects.
    val height: Float = if (isToolbar) 0.35f else 0.1f
    var distanceFromUser: Float = if (bigPanel) 0.9f else 0.7f

    // Having the users position, we place the entity in front of it, at a particular distance and height
    var newPos = headPose.t + headPose.q * Vector3.Forward * distanceFromUser
    newPos.y = headPose.t.y -height

    // If there is an offset vector, we place the object at the vector position (using user's position as reference)
    if (offset != Vector3(0f)) {
        newPos = headPose.t +
                headPose.q * Vector3.Right * offset.x +
                headPose.q * Vector3.Forward * offset.z

        newPos.y = headPose.t.y + offset.y
    }

    // Add rotation to look in same vector direction as user
    var newRot = Quaternion.lookRotation(newPos - headPose.t)
    // Rotate 180 degrees to face user in case of non panel objects
    if (nonPanel) newRot *= Quaternion(0f, 180f, 0f)

    entity.setComponent(Transform(Pose(newPos, newRot)))
}

Create panels and interaction with spatial objects

Focus Projects features 12 main spatial panels, including the Home Panel, the Toolbar, and its sub-panels. Users can create additional panels by generating Sticky Notes, Timers, WebViews, or Spatial Tasks.
You can create panels, make them grabbable, and give them functionality.
You can create panels, make them grabbable, and give them functionality.

Create transparency and spatial text panels

To effectively use spatial text, design a new theme style and set the windowBackground property to transparent, creating a panel with a transparent background.
//themes.xml
<style name="Theme.Focus.Transparent" parent="Base.Theme.Focus">
…
  <item name="android:windowBackground">@android:color/transparent</item>
…
 </style>
Next, apply the theme to the panel configuration. Set the includeGlass attribute to false in the PanelConfigOptions if you want to hide the panel.
val panelRegistration = PanelRegistration( R.layout.activity_main) {
    config {
        themeResourceId = R.style.Theme_Focus_Transparent
        width = 0.5f
        height = 0.5f
        includeGlass = false
    }
    activityClass = MainActivity::class.java
}
Demonstrating transparency and spatial text panels
To attach a spatial text to an object, see the explanation of composed objects.
Did you find this page helpful?
Thumbs up icon
Thumbs down icon