syncPose(gripPose) {
    let visible = false
    if (gripPose) {
      // Get current posed camera position, relative to the parent
      let cam = this.getCameraFacade().threeObject
      camPos.setFromMatrixPosition(cam.matrixWorld)
        .applyMatrix4(invertMatrix4(this.threeObject.parent.matrixWorld, tempMat4))

      // Find target position
      let targetScale
      if (this.active) {
        // Find direction vector and lengthen it to the target distance to find base position
        targetPos.copy(gripPose.transform.position)
        targetPos.y = camPos.y
        targetPos.sub(camPos)
        targetPos.setLength(Math.max(this.minDistanceFromCamera, targetPos.length() + this.distancePastGrip))
        targetPos.add(camPos)
        targetPos.y = gripPose.transform.position.y + this.heightAboveGrip
        targetScale = 1
      } else {
        targetPos.copy(gripPose.transform.position)
        targetScale = 0.001
      }

      // Pull partway toward target position and scale, like a spring
      let pos = this.threeObject.position
      pos.lerp(targetPos, 0.05) //move by 5% of distance each frame)
      this.scale += (targetScale - this.scale) * 0.3
      visible = this.scale > 0.01 //hide below a certain size

      if (visible) {
        // Rotate to face camera
        this.rotateY = Math.atan2(camPos.x - pos.x, camPos.z - pos.z)

        // Update projection cone's source position
        let proj = this.getChildByKey('$projection')
        if (proj) {
          proj.syncSourcePosition()
        }

        // Sync all matrices
        this.traverse(updateMatrices)
      }
    }
    if (visible !== this.visible) {
      this.update({visible})
    }
  }
Sync to the current XRFrame's gripPose - all matrix syncing is localized here
to avoid a full afterUpdate pass on every frame.