Skip to content
SDK Docs
Discord Order Now

On the glasses

Drawing

Draw shapes, text and small images on the 640 × 480 display.

The canvas

  • Coordinates start at the top-left corner. x grows to the right and y grows downward.
  • Every color is a gray level from 0 to 15. 0 is black, which shows nothing on the display, and 15 is full brightness.
  • Anything outside the canvas is clipped. Nothing is scaled.
Hello, MIRA (0, 0) (639, 479)
The example below, drawn on the 640 × 480 canvas.

The 16 levels, from 0 (off) to 15 (brightest).

Frames

Draw each frame between begin() and present(). Everything you draw after begin() replaces the previous frame when you call present(). There’s no separate clear or end call.

MIRA compares each frame with the last one and repaints only what changed, so you don’t need to redraw on every app_render call. Redraw when your state changes.

Draw a card
#[link(wasm_import_module = "mira")]
extern "C" {
    fn begin();
    fn fill_round_rect(x: i32, y: i32, w: i32, h: i32, radius: i32, gray: i32);
    fn text(x: i32, y: i32, w: i32, h: i32, gray: i32, font: i32, align: i32,
            line_spacing: i32, utf8: *const u8, len: i32);
    fn present();
}

unsafe fn draw() {
    let label = b"Hello, MIRA";
    begin();
    fill_round_rect(120, 176, 400, 128, 20, 3); // dim card
    text(120, 220, 400, 48, 15, 2, 1, 40, label.as_ptr(), label.len() as i32);
    present();
}

Shapes

FunctionDraws
line(x0, y0, x1, y1, width, gray)A line, width pixels thick
fill_rect(x, y, w, h, gray)A filled rectangle
stroke_rect(x, y, w, h, line, gray)A rectangle outline, line pixels thick
fill_round_rect(x, y, w, h, radius, gray)A filled rounded rectangle
stroke_round_rect(x, y, w, h, radius, line, gray)A rounded rectangle outline
fill_circle(cx, cy, radius, gray)A filled circle
stroke_circle(cx, cy, radius, line, gray)A circle outline
fill_triangle(x0, y0, x1, y1, x2, y2, gray)A filled triangle

Text

text(x, y, w, h, gray, font, align, line_spacing, utf8, len) draws UTF-8 text inside a box and clips it to that box. line_spacing is the line height in pixels. MIRA copies the text before the call returns, so you can reuse the buffer right away.

FontSizeAlignMeaning
024 px0Left
118 px1Center
232 px2Right
340 px

MIRA doesn’t wrap long text for you. Measure and split it into lines that fit, the way the shared text helpers in the guide do.

Images

4-bit images

bitmap4(x, y, w, h, pixels, len) draws a grayscale image with 16 levels.

  • Each row is ceil(w / 2) bytes, with the left pixel in the high nibble. Pad odd widths with the low nibble.
  • len must equal ceil(w / 2) * h. Images can be up to 640 × 480 and are drawn at their real size.
  • Gray 15 is transparent, so use 14 for the brightest visible pixel.

bitmap4_rotated(x, y, w, h, pixels, len, degrees) takes the same data and rotates the image around its center by whole degrees.

1-bit masks

bitmap1(x, y, w, h, mask, len, gray) draws a one-color mask up to 32 × 32 in the gray level you pass. Each row is ceil(w / 8) bytes, most significant bit first. Masks are a good fit for icons.

Retained frames

On text-heavy screens, draw the static part once and keep it. frame_checkpoint() saves everything drawn so far in the frame. On later frames, frame_restore() brings it back so you only draw what changed on top.

frame_restore() returns 0 when there’s nothing to restore, for example right after a switch, so redraw everything in that case. Frames that contain 4-bit images can’t be checkpointed; 1-bit masks can. Shared/retained_frame.rs wraps the pattern:

Retained frame
#[path = "../Shared/retained_frame.rs"] mod retained_frame;

unsafe fn draw(page_changed: bool) {
    if retained_frame::start(page_changed) {
        draw_page();                 // only when the page changed
        retained_frame::checkpoint();
    }
    draw_clock();                    // small parts that change every frame
    present();
}