Glassly Miniapp SDK betaThe SDK is in beta, so its APIs may change before general availability.Developing on Mentra Live? We recommend using the Glassly Bluetooth SDK.The developer tools are available in the Glassly App under Settings → Miniapp Developer Settings.Share feedback with an in-app bug report, on Discord, or by email at help@glassly.ai.
session.display.render() draws to the glasses HUD. Each call describes the whole frame: everything that should be on screen right now. The phone diffs it against the previous frame, so elements with a stable id update in place (no flicker), anything you leave out of the new frame comes off the screen, and render([]) clears it. Your job is to describe the frame; the phone works out what changed.
Display needs no permission, but the glasses do need a screen. Declare a DISPLAY hardware requirement in your manifest so the miniapp only installs on glasses that can show anything.

Elements

Shapes beyond rect render where capabilities.display.shapes lists the type (the Even Realities G2 with the Glassly firmware draws all of them on the glasses); elsewhere they are dropped and reported. color is a gray level 0..15. Points are raw canvas pixels and are not anchored.

Transitions

Give an element a stable id and a transition, and geometry changes between renders animate on devices with capabilities.display.animation:
Moves, resizes, point and angle changes, color and stroke widths tween; new text or image pixels switch immediately. Devices without animation jump. Text with a stable id can therefore tick, scroll or fade at tens of updates per second: re-render with the new string and, optionally, a new box.

Screen sizes

box is {x, y, w, h} in raw device pixels. Coordinates map 1:1 to the device canvas, with the origin at the top-left: We recommend designing your layout for 500 × 220. A frame that fits 500 × 220 renders as-is on every positioning device; on the G2’s larger canvas it sits in the top-left with some margin. Boxes that run past the edge are clamped, and elements past the device’s budget are dropped tail-first. Both show up in the render result, so you find out.

Anchoring to a corner

For elements that hug a screen corner (a minimap bottom-right, a status glyph bottom-left), give the box an anchor. x/y then become insets from that corner, growing inward, and the phone resolves the box against the connected device’s real canvas — the element lands flush on every resolution with nothing to clamp:
anchor is one of "top-left" (the default), "top-right", "bottom-left", "bottom-right". Corners only — there is no center or edge anchoring, and no scaling. When you want an exact fit instead (a full-screen caption), compute boxes from the connected device’s real canvas. session.capabilities.display carries it, populated on the "ready" event:

Older glasses

Even Realities G1 and Vuzix Z100 show text but can’t place elements at coordinates, and their displays can’t show bitmaps. Render the same scene anyway: the phone converts it for them. Text elements collapse into a single full-view text layout in reading order (top to bottom, then left to right), and image and rect elements are dropped. The result tells you what happened (degraded: true, plus the dropped ids), and session.capabilities.display.canPosition tells you up front which kind of device you’re on. In practice a text-first miniapp works everywhere with zero branching: a caption app’s single full-canvas text element reads the same on every device. If your layout leans on positioning or images, like a HUD with a minimap, check canPosition and render a text-only variant for these devices.

Updating and clearing

Give any element that changes over time a stable id. Re-rendering with the same id updates that element in place on the glasses, and unchanged elements never re-cross Bluetooth, so pushing the whole frame on every change is cheap. To take something off screen, leave it out of the next frame. To clear everything:

Options and results

render(elements, options?) takes {view, durationMs}view picks "main" (default) or "dashboard", and durationMs auto-clears the frame after that many milliseconds. Awaiting is opt-in. A plain fire-and-forget call is fine, and the returned promise never rejects:

Just showing text

The simplest useful frame is one text element covering the whole canvas — a status line, a caption, a “Saved” confirmation:
Per-element style.breakMode ("character" | "character-no-hyphen" | "word" | "strict-word") controls how the text wraps inside its box.

Dashboard view

render(elements, {view: "dashboard"}) targets the dashboard view, the glasses’ status view that the user brings up with a glasses-specific gesture (head-up on G1, a double-tap on others). The default is "main".