Format and annotate
Choose the encoder, background, and quality, then add the axis indicator, label, and scale bar that let a render stand on its own.
Five choices decide what leaves the renderer: format, background, quality, annotations, and the dimensions annotations require.
1. Pick the format
import { renderImage } from 'nanoraster';const image = await renderImage(glb, { format: 'webp', quality: 1,});Rendering…
Switch format: the badge's MIME type and bytes change, the pixels do not.
| Format | Alpha | Lossy | Use for |
|---|---|---|---|
png | Yes | No | Exact pixels, diffing, transparent thumbnails |
webp | Yes | Only when quality is below 1 | Web delivery; lossless by default, optionally lossy |
jpeg | No | Yes | Interop with tools that accept nothing else |
jpg | No | Yes | Alias for jpeg; the filename keeps the jpg extension |
raw | Yes | No | The frame itself, for a diff, a video frame or a texture |
mimeType reflects the true type, so jpg and jpeg both report image/jpeg;
the full MIME map is in the reference. raw runs no
encoder; see Work with raw pixels.
2. Decide on a background
import { renderImage } from 'nanoraster';const image = await renderImage(glb, { format: 'webp', background: '#101418',});Rendering…
The default background is transparent, which PNG, WebP and raw can
represent and JPEG cannot. A JPEG request must set an opaque background;
omitting it fails with an
encode failure rather than silently flattening onto
an arbitrary colour. Backgrounds accept #RRGGBB, #RRGGBBAA, or normalised
straight-alpha RGBA such as [0.05, 0.06, 0.08, 1].
3. Tune quality per format
quality runs from 0 to 1 and means something different per format:
- JPEG is always lossy;
qualitysets the compression level and defaults to0.92. - WebP defaults to
1, which encodes lossless. Any value below1switches to the lossy codec, which keeps alpha but never reaches exact pixels; this matches Chrome's canvastoBlob('image/webp', quality). - PNG and raw ignore
quality.
Drag quality below 1 on the tile in step 1 and watch the badge: the file
shrinks and the pixels stop being exact.
4. Add an axis indicator, label and scale
Drawing orientation, a label and a physical scale into the image keeps the render's context once it is separated from the request.
import { renderImage } from 'nanoraster';const image = await renderImage(glb, { format: 'webp', width: 512, height: 512, axes: true, scaleBar: true, label: 'gear',});Rendering…
The axis indicator is a camera-aware XYZ marker in the bottom-right; it rotates with the camera, so it reports the view's orientation rather than a fixed key.
The label is drawn top-left, screen-upright, verbatim. Setting label is what
draws it; in a batch call, a view is labelled when its
own entry sets label.
The scale bar is drawn bottom-left in the model's own units. Under perspective
it is only accurate at one depth, so its label names that plane with
@ center; under orthographic projection scale is
depth-invariant and the qualifier is dropped.
5. Meet the minimum dimensions
Annotations need room to stay legible, so enabling any of them raises the minimum output size to 192 pixels on both axes. A smaller request fails validation rather than producing an unreadable overlay.
const image = await renderImage(glb, {
format: 'webp',
width: 192,
height: 192,
axes: true,
});Below that, drop the annotations.
Variations
Smallest useful thumbnail. WebP at quality: 0.9 with width: 256 is
around 60 percent smaller than the lossless default and a third the size of
the equivalent PNG, with transparency kept. Sparse line art can still encode
smaller lossless than lossy, so measure both when every byte matters.
Byte-exact comparison. Any lossless format works: PNG, or WebP at the
default quality: 1. A lossy baseline is
deterministic but diffs the encoder's losses
along with the render; format: 'raw' removes the
encoder from the comparison.
Alpha over an unknown page background. Use PNG or WebP and leave the background transparent, then let the page composite it.
Annotations plus a tight fit. Overlays are drawn inside the frame, so a
margin near 0 can put the subject underneath them. Keep the default margin
when annotating.
Reuse the renderer
Keep one GPU device alive across renders with createRenderer, dispose it deliberately, and know when a single plan call is the better tool.
Work with raw pixels
Return the RGBA frame instead of an encoded file, for pixel diffs, video frames and texture uploads with no image decoder in the loop.