Skip to content
SDK Docs
Discord Order Now

On the glasses

Head tracking

Read head orientation and motion right on the glasses.

There’s no phone round trip, so head tracking is fast enough for games and steering.

Orientation

FunctionReturns
imu_yaw_cdeg()Heading. Positive is turning right.
imu_pitch_cdeg()Tilt. Positive is looking up.
imu_roll_cdeg()Roll.
imu_ready()1 once a calibrated sample has arrived, otherwise 0.

Angles are in centidegrees: 100 is one degree. Yaw wraps at ±180°, so always take the shortest signed difference between two headings.

Steer with your head
#[link(wasm_import_module = "mira")]
extern "C" {
    fn imu_yaw_cdeg() -> i32;
    fn imu_pitch_cdeg() -> i32;
    fn imu_ready() -> i32;
}

static mut CENTER: Option<(i32, i32)> = None;

// Shortest signed difference, in centidegrees.
fn wrap(d: i32) -> i32 {
    let d = d % 36_000;
    if d > 18_000 { d - 36_000 } else if d < -18_000 { d + 36_000 } else { d }
}

unsafe fn pointer_position() -> Option<(i32, i32)> {
    let (yaw, pitch) = (imu_yaw_cdeg(), imu_pitch_cdeg()); // call first
    if imu_ready() == 0 { return None; }
    let (yaw0, pitch0) = *CENTER.get_or_insert((yaw, pitch));
    let x = 320 + wrap(yaw - yaw0) * 8 / 100;   // 8 px per degree
    let y = 240 - (pitch - pitch0) * 8 / 100;   // up is negative y
    Some((x.clamp(0, 639), y.clamp(0, 479)))
}

Screen y grows downward while positive pitch means looking up, which is why the example subtracts pitch. Recenter by storing a new starting heading, for example when the wearer taps.

Sharing the sensor with MIRA

While your app reads orientation, MIRA pauses its look-up dashboard so a glance upward doesn’t interrupt your game. When you’re done with head tracking, call imu_stop() (content::imu::stop() in Rust) to give it back. Leaving the app releases it automatically.

If you only need to know whether the wearer is looking up or down, use imu_threshold(above_cdeg, below_cdeg). It doesn’t take the sensor away from MIRA, so the dashboard keeps working.

imu_threshold returnsMeaning
1Above the upper threshold
0Below the lower threshold
-1Display asleep, or no calibrated data yet
-2Full head tracking is active, by this app or by the phone

Raw motion

imu_raw_read(out) writes nine 32-bit floats (36 bytes): accelerometer x, y, z, then gyroscope x, y, z, then magnetometer x, y, z. The values come straight from the sensors, with no filtering or fusion.

It returns 1 when the sample is fresh (at most 500 ms old), 0 when it’s unavailable or stale, and -1 for an invalid buffer. Only use the values when it returns 1. Like the orientation functions, reading raw motion pauses the look-up dashboard until you call imu_stop().

From the phone

Your phone companion can also read motion with miraCompanion.getIMU() and release it with miraCompanion.stopIMU(). Phone orientation readings are in degrees, their pitch sign is the reverse of the glasses functions, and they arrive over Bluetooth. Poll them at a modest rate (every 400 ms or so) and use the glasses functions above for anything that needs low latency. See Phone companion.