Skip to main content
4 min read

Drawing API (c.*)

Every page function receives a canvas c. You never touch pixels directly. You describe a picture by calling c.* methods, and GDN's trusted renderer turns that description into a PNG (and then into the panel's native format).

def main(c, ctx):
c.fill("black")
c.rect(0, 0, c.width - 1, 7, fill="green")
c.text("GLANCE", 4, 1, font="5x7", color="black")
c.text(ctx.inputs.get("msg", "HELLO").upper(), 4, 12, font="5x7", color="white")

The canvas & coordinates

The coordinate system

  • Origin (0, 0) is the top-left pixel. x grows right, y grows down.
  • For c.text(...), c.image(...) and c.bitmap(...), (x, y) is the top-left corner of what you draw; the text/image extends right and down from that point. So c.image("pawn.png", 4, 3) puts the image's top-left corner 4 pixels from the left edge and 3 from the top.
  • The one exception is circles: c.circle(cx, cy, r...) centers on (cx, cy).
  • The panel is always 32 pixels tall. A single panel is 64 pixels wide and panels daisy-chain for wider displays, up to 384 pixels wide; for best performance keep images to 192 pixels wide or smaller and split content across pages. Width is whatever your manifest sets.
  • c.width and c.height give the canvas size; use them instead of hard-coding.
  • Anything drawn past the edges is safely clipped, no errors, it just doesn't show.

Colors

Anywhere a color is accepted you can pass a name ("green", "white", "red", "amber", …) or a hex string ("#00FF00", "#FC0"). Glance's brand green is #00FF00. The global color struct has all the named constants plus color.dim(c, pct) for darkened variants, see Helper functions.

Every named color, as it looks on the panel — click a chip to copy its name (gray/darkgray/midgray also answer to the grey spellings):

Text is UPPERCASE

The bitmap fonts contain capital letters, digits, and punctuation, but no lowercase glyphs. Lowercase characters are silently skipped, so c.text("hello", …) draws nothing. Write strings in caps, and .upper() anything that comes from an input.

Methods

CallWhat it does
c.fill(color)Fill the whole canvas with one color (usually your first call).
c.pixel(x, y, color)Set a single pixel.
c.rect(x0, y0, x1, y1, fill=None, outline=None)Inclusive rectangle from (x0,y0) to (x1,y1). Pass fill, outline, or both.
c.line(x0, y0, x1, y1, color)Straight line, inclusive endpoints.
c.text(s, x, y, font="5x7", color="white", align="left")Draw text. align is left, center, or right.
c.text_stroke(s, x, y, font="5x7", color="white", stroke="black", thickness=1, align="left")Text with an outline around every glyph, for legibility over filled or busy backgrounds. See Outlined text.
c.text_width(s, font="5x7")Measure a string's pixel width (for layout/centering).
c.bitmap(matrix, x, y, color)Draw pixel art: a 2D list of 0/1; each 1 lights a pixel in color.
c.image(name, x, y, w=None, h=None)Paste a bundled PNG (declared in assets:). w/h resize with crisp nearest-neighbor.

These are the primitives. On top of them, c also has higher-level helpers: circles, triangles, rounded rects, centered/wrapped/auto-sized text, progress bars, sparklines, badges, and trend arrows, documented on the Helper functions page.

All drawing methods return c, so you can chain them:

c.fill("black").text("HI", 2, 2, color="green").rect(0, 10, 30, 10, fill="red")

Measuring & centering

w = c.text_width("SCORE", font="7x12")
c.text("SCORE", (c.width - w) // 2, 4, font="7x12", color="amber")
# …or just:
c.text("SCORE", c.width // 2, 4, font="7x12", color="amber", align="center")

Outlined text

c.text_stroke is c.text with a border: it draws the string once in stroke at every offset within thickness pixels, then once more in color on top. It mirrors the panel's own drawTextWithStroke, so what you see in Studio is what the LEDs show.

# contrast on a filled background: the default black stroke
c.gradient_rect(0, 0, c.width - 1, c.height - 1, "#1E3350", "#0A1220", horizontal=False)
c.text_stroke("SUNRISE 6:42", 6, 4, font="6x8", color="white")

# a colored outline as a design choice
c.text_stroke("BLUE OUTLINE", 4, 3, font="6x8", color="white", stroke="#2F6FDC")
c.text_stroke("THICK", 4, 17, font="6x8", color="#FFE8A8", stroke="#7521F9", thickness=2)
Text on a striped gradient without a stroke, blending into the backgroundThe same text with a black one-pixel stroke, cleanly separated
Left: c.text on a filled background. Right: c.text_stroke, default black stroke.

When to use it, and when black is the right stroke, is on the Type, color & stroke page. The 10x15_outline and 10x16_outline fonts carry a border in the glyphs themselves — a hero face that needs no stroke call.

Pixel-art bitmaps

HEART = [
[0,1,0,1,0],
[1,1,1,1,1],
[0,1,1,1,0],
[0,0,1,0,0],
]
c.bitmap(HEART, 12, 13, color="red")

See also: Fonts · Working with images · Design guidelines.