Skip to content

Last updated: 2026-07-04

The glasses don't render arbitrary HTML. They composite a fixed canvas from a small set of SDK container objects, each placed by absolute pixel coordinates.

Canvas

Each eye displays a 576 x 288 pixel canvas. The coordinate origin is the top-left corner. X increases to the right; Y increases downward.

Color is 4-bit greyscale - 16 levels of green. White pixels show as bright green; black pixels are off (transparent).

Containers

The UI is built from containers - rectangular regions placed by absolute pixel coordinates. No CSS, no flexbox, no DOM.

Rules:

  • At most 4 image containers and 8 other containers per page (mix freely).
  • Exactly one container has isEventCapture: 1. It receives all input events.
  • Containers can overlap. Since SDK 0.0.12, zOrderIndex controls stacking explicitly - see Stacking order. Pages that omit it keep the old rule: later declarations draw on top.

Shared properties

PropertyTypeRangeNotes
xPositionnumber0–576Left edge (px)
yPositionnumber0–288Top edge (px)
widthnumber0–576Container width (px)
heightnumber0–288Container height (px)
containerIDnumber-Unique per page
containerNamestringmax 16 charsUnique per page
isEventCapturenumber0 or 1Exactly one must be 1
zOrderIndexnumberunique per pageStacking - larger renders in front (SDK 0.0.12+). All-or-nothing per page - see Stacking order

Border properties

Available on text and list containers only:

PropertyTypeRangeNotes
borderWidthnumber0–50 = no border
borderColornumber0–15 / 0–16Greyscale level
borderRadiusnumber0–10Rounded corners (note: typo preserved from SDK protobuf)
paddingLengthnumber0–32Uniform padding on all sides

There is no background or fill property. The border is the only visual decoration.

Stacking order (zOrderIndex)

Since SDK 0.0.12, list, text, and image containers take an optional zOrderIndex in both createStartUpPageContainer and rebuildPageContainer. Larger values render closer to the front.

Rules:

  • All-or-nothing. If any container on a page sets zOrderIndex, every list, text, and image container on that page must set it. Omit it on all of them and the page keeps declaration-order stacking - pages built against SDK ≤ 0.0.11 behave exactly as before.
  • Unique per page. No two containers on the same page may share a value. There is no same-value tie-break.
  • Rendering only. zOrderIndex doesn't touch input routing - the isEventCapture rule (exactly one per page) applies unchanged.

Violations never reach the glasses: the SDK validates before calling native, logs an EvenHubPageContainerValidationErrorCode error, and createStartUpPageContainer returns StartUpPageCreateResult.invalid (rebuildPageContainer returns false).

The layout this unlocks - an image as the backdrop, text floating in front, stable across rebuilds:

typescript
await bridge.createStartUpPageContainer({
  containerTotalNum: 2,
  imageObject: [{
    xPosition: 144,
    yPosition: 72,
    width: 288,
    height: 144,
    containerID: 1,
    containerName: 'backdrop',
    zOrderIndex: 1, // back
  }],
  textObject: [{
    xPosition: 168,
    yPosition: 112,
    width: 240,
    height: 64,
    containerID: 2,
    containerName: 'caption',
    content: 'Now playing',
    isEventCapture: 1,
    zOrderIndex: 2, // front
  }],
})

The image container starts empty - push pixels with updateImageRawData after the page is created.

Text containers

The primary container type. Plain text, left-aligned, top-aligned. No alignment options, no font-size control, no bold or italic.

typescript
new TextContainerProperty({
  xPosition: 0,
  yPosition: 0,
  width: 576,
  height: 288,
  borderWidth: 0,
  borderColor: 5,
  paddingLength: 4,
  containerID: 1,
  containerName: 'main',
  content: 'Your text here',
  isEventCapture: 1,
})

Content limits

MethodMax Characters
createStartUpPageContainer1,000
textContainerUpgrade2,000
rebuildPageContainer1,000

Behavior

  • Text wraps at the container width.
  • If content overflows and the container has isEventCapture: 1, the firmware scrolls it.
  • \n is a line break.
  • Unicode works as long as the glyph is in the firmware's font set.
  • A full-screen text container holds roughly 400-500 characters.
  • "Centering" means padding with spaces.

In-place updates

Reach for textContainerUpgrade - it's faster than a full rebuild and flicker-free on hardware. Pass a TextContainerUpgrade instance. containerID, containerName, and content are the only required fields for a basic update; contentOffset and contentLength are for partial-string updates.

typescript
import { TextContainerUpgrade } from '@evenrealities/even_hub_sdk'

await bridge.textContainerUpgrade(new TextContainerUpgrade({
  containerID: 1,
  containerName: 'main',
  content: 'Updated text',
}))

List containers

Native scrollable lists, with scroll highlighting handled in firmware.

  • Up to 20 items per list.
  • Up to 64 characters per item.
  • No per-item styling, no row-height control, no separators.
  • No in-place updates - changing a list means rebuilding the whole page.

Image containers

Render greyscale images.

  • Up to 288 x 144 px per container (width x height).
  • 4-bit greyscale.
  • Accepts number[], Uint8Array, ArrayBuffer, or base64.
  • Cannot send during createStartUpPageContainer - create a placeholder, then update via updateImageRawData.
  • No concurrent image sends.
  • Since SDK 0.0.12, raw data is LZ4-compressed in transit automatically - zero code change. Transfers shrink and image updates land faster.

Image-first apps: put a full-screen text container with content: ' ' and isEventCapture: 1 behind the image container - lower zOrderIndex, or declared first if the page omits zOrderIndex. The text container collects input; the image draws on top.

Font and Unicode support

The glasses ship a single LVGL font baked into firmware. No font selection, no size control, not monospaced. Characters outside the font set are silently dropped.

The Useful Characters for Building UIs section of the design guidelines has a lookup table of glyphs that ship in the set.