Skip to content
SDK Docs
Discord Order Now

On the phone

Phone companion

Give your app a screen in the MIRA iPhone app, with messaging, storage and networking.

The HTML, CSS and JavaScript in your app’s file becomes its screen in the MIRA iPhone app. It runs while your app runs, and it’s the right place for settings, text entry, network calls and anything tied to your accounts.

The bridge

MIRA adds a miraCompanion object to your page. On the glasses, Shared/companion.rs handles registration, paging, acknowledgements and message framing, so both sides work with whole JSON messages.

Send and receive messages

Here the phone sends ping and the glasses answer pong:

const status = document.getElementById("status");

const stop = miraCompanion.onFromApp((message) => {
  if (message.type === "pong") status.textContent = "The glasses replied";
});

document.getElementById("ping").onclick = () =>
  miraCompanion.sendToApp({ type: "ping" });

window.addEventListener("pagehide", stop);
  • Messages from the phone to the glasses can be up to 3,500 bytes of JSON. Messages from the glasses to the phone can be up to 4,096 bytes.
  • sendToApp resolves once MIRA accepts the message, not when the glasses have handled it. It rejects when the message can’t be accepted, so you can retry later.
  • Call tick on every frame and handle each message it returns exactly once.

Choosing a channel

MethodUse it for
sendToApp(value)Commands and events that must all arrive, in order.
setSnapshotForApp(value)State the glasses page through, like a list or an article, up to 3,500 bytes. A newer snapshot replaces one that hasn’t been delivered yet, without dropping your ordered messages.
setLatestForApp(value)Small state that changes quickly, like a game scene, up to 380 bytes. Only the newest value is kept. Pair it with companion::Realtime on the glasses, which sends controls back in up to 379 bytes.

Storage

Save small strings on the phone so they’re still there next time:

Storage
await miraCompanion.storeSet("timer.settings", JSON.stringify({ minutes: 25 }));

const saved = JSON.parse((await miraCompanion.storeGet("timer.settings")) || "{}");

await miraCompanion.storeRemove("timer.settings");

storeGet resolves to the saved string, or null if there isn’t one. Prefix your keys with your app’s name, and don’t store passwords, tokens or personal data this way.

Network

Use the standard fetch() for HTTP and HTTPS. MIRA makes the request from the phone, so browser CORS rules don’t get in the way, and cookies aren’t stored between requests. Request and response bodies can be up to 10 MB, and up to 8 requests run at once while the rest wait their turn.

fetch
const response = await fetch("https://api.example.com/weather?city=Paris");
const weather = await response.json();
miraCompanion.setSnapshotForApp({ type: "weather", temp: weather.temp });

WebSocket works as usual for live connections. Links your page opens with <a href> open in Safari.

Device data

Any running companion can call these:

MethodReturns
getMediaState(){available, title, artist, playing, elapsedMs, durationMs, volume} for what’s playing. volume is the phone’s output level in percent.
mediaCommand(code){accepted}. Codes: 0 previous, 1 play or pause, 2 next, 3 volume up, 4 volume down.
getIMU()The latest motion readings, with accelerometer, gyroscope, magnetometer and attitude. Treat missing fields as unavailable.
stopIMU()Stops the motion stream your companion started.

All four are asynchronous. For head control in games, read orientation on the glasses instead; see Head tracking.

Paste button

For text entry, miraCompanion.bindPasteButton(button, onPaste) shows the iOS Paste control over one of your buttons. onPaste(text) receives plain text when the wearer pastes, without an extra clipboard prompt. The binding has a ready promise and a dispose() method, and it’s removed automatically when the page goes away.

Lifecycle

  • Your companion runs while your app runs. Tap your app’s card in Your Apps to bring its screen up.
  • Closing the phone sheet doesn’t stop the companion. Stop anything you started, like microphone capture, when your app no longer needs it.
  • JavaScript can pause while the MIRA app is in the background, so send the glasses what they need ahead of time instead of streaming it at the last moment.