Shapes and text
Alongside sprites, spryte draws filled rectangles, circles and lines, and text in CSS fonts or bitmap fonts. They batch with everything else, in call order.
draw.rect(10, 10, 100, 8, "#333"); // health bar background
draw.rect(10, 10, 100 * hp, 8, "#cffb2d"); // health bar fill
draw.circle(160, 90, 24, "#ff5c8a"); // filled, antialiased
// 2 units wide, half transparent
draw.line(0, 0, 320, 180, 2, "#ffffff80");
draw.text("score 120", 8, 24, { color: "#fbbf24" });Shapes
| Method | |
|---|---|
rect(x, y, w, h, color) | Filled rectangle from its top-left corner. |
circle(x, y, r, color) | Filled, antialiased circle around its centre. |
line(x1, y1, x2, y2, width, color) | A line, drawn as a rotated rectangle. A zero-length line draws nothing. |
All three follow the current transform and the global draw.alpha (see transforms). Shapes are filled only. For an outline, draw four thin rects or lines.
Colours
Anywhere spryte takes a colour (rect, circle, line, text's color, a sprite's tint, the clear colour in begin()), you can pass:
- A CSS hex string:
"#rgb","#rgba","#rrggbb"or"#rrggbbaa". - A number
0xRRGGBBAA, such as0xcffb2dff. - Any other CSS colour, such as
"rebeccapurple"or"rgb(255 0 0 / 50%)". These are resolved once through a canvas, then cached.
Hex strings and numbers are the cheapest. spryte also exports toRGBA(color), which returns the packed 0xRRGGBBAA number, and rgbaToCss(rgba) for the reverse.
Text with CSS fonts
draw.text("Game over", 160, 90, {
font: "bold 24px system-ui",
color: "#ffffff",
align: "center",
baseline: "middle",
});| Option | Default | |
|---|---|---|
font | draw.font | A CSS font string or a BitmapFont. |
color | "#ffffff" | Text colour. |
align | "left" | "left", "center" or "right" of x. |
baseline | "top" | "top", "middle" or "bottom" of y. |
alpha | 1 | Opacity, multiplied with draw.alpha. |
CSS text is rasterised once and cached as a texture (an LRU cache of 256 entries), at the scale you passed to begin(), so it's crisp on high-DPI screens. Text that changes every frame, such as a timer with milliseconds, fills the cache with new entries; old ones are evicted automatically.
Make sure a web font has loaded before you draw with it, for example with await document.fonts.load("24px Pixelify"). Otherwise the first rasterisation uses a fallback font and is cached that way.
The default font
draw.font is used when text() or measure() gets no font. It defaults to "16px sans-serif" and can be a CSS string, a BitmapFont, or a function returning one. A function is called on first use, so a font built from a canvas costs nothing until text is drawn.
draw.font = "14px monospace";
draw.text("hp 3", 4, 4); // uses 14px monospaceBitmap fonts
A BitmapFont draws text from a sprite sheet of glyphs. Glyphs land on whole pixels, so it stays crisp at any integer scale: the right choice for pixel art.
import { BitmapFont, type Frame } from "spryte";
const sheet = await load("/font.png"); // glyphs on a 6×9 grid, 16 per row
const frames: Frame[] = [];
for (let i = 0; i < 95; i++) frames.push({ image: sheet, x: (i % 16) * 6, y: Math.floor(i / 16) * 9, w: 6, h: 9 });
const font = new BitmapFont({ frames, chars: BitmapFont.ASCII });
draw.text("coins 3", 4, 4, { font, color: "#fbbf24" });BitmapFont.ASCII is printable ASCII from space to ~, the order most font sheets use. The constructor takes:
| Option | Default | |
|---|---|---|
frames | required | One frame per glyph, in the order of chars. |
chars | required | The characters the frames hold. |
widths | measured | Advance per glyph in pixels. By default it's measured from each glyph's rightmost opaque column, so a grid becomes a proportional font. Pass a number for a monospaced font. |
spacing | 1 | Pixels between glyphs. |
spaceWidth | a third of the cell height, rounded | Advance for a space, and for any glyph that measures empty. |
lineHeight | cell height + 1 | Pixels from one line's top to the next. |
fallback | "?" if present | Drawn for characters the font lacks. |
Bitmap text supports "\n" for multiple lines, and the same color, align, baseline and alpha options.
Measuring
draw.measure(text, font?) returns the logical { w, h } box that text() fills, so you can lay out and centre without guessing:
const label = "Start";
const { w, h } = draw.measure(label, "20px system-ui");
draw.rect(160 - w / 2 - 6, 120 - 4, w + 12, h + 8, "#222");
draw.text(label, 160, 120, { font: "20px system-ui", align: "center" });Measuring uses the same cache as text(), so measuring and then drawing rasterises once.
Next: Transforms and clipping