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 and watch the badge. The MIME type and byte count change. The
pixels do not change.
| 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 reports the true type, so jpg and jpeg both report image/jpeg.
The reference lists the full MIME map. 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. PNG, WebP and raw can represent
transparency; JPEG cannot. A JPEG request without an opaque background fails
with an encode failure; the renderer does not
silently flatten 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. The lossy codec 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. The file shrinks and the
pixels stop being exact.
4. Add an axis indicator, label and scale
Draw orientation, a label and a physical scale into the image. These annotations keep the render's context after it leaves 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 an XYZ marker in the bottom-right corner. It rotates with the camera, so it reports the view's orientation rather than a fixed key.
The renderer draws the label top-left, screen-upright, verbatim, and only
when the request sets label. In a
batch call, a view receives a label only when its own
entry sets label.
The renderer draws the scale bar bottom-left in the model's own units. Under
perspective, the scale bar is accurate at only one depth. A fitted camera
labels that depth @ center; a fixed camera labels its caller-owned
@ target. Under orthographic projection, scale is
depth-invariant, so the label drops the qualifier. Annotations may loosen a
fitted camera but never change a fixed camera.
5. Set authored-edge thickness
lineWidth controls glTF LINES primitives in output pixels, from 0.25 to
16. The default is 3. The width does not grow with image height: a 1 px line
stays 1 px at 2048×2048. Meshes and lines use 4× MSAA.
const image = await renderImage(glb, { format: 'png', lineWidth: 1 });6. Meet the minimum dimensions
Enabling any annotation 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 size, drop the annotations.
Variations
Smallest useful thumbnail. WebP at quality: 0.9 with width: 256 is
around 60 percent smaller than the lossless default. It is a third the size
of the equivalent PNG, and it keeps transparency. 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 a diff then includes 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. The renderer draws overlays inside the
frame, so a margin near 0 can put the subject underneath them. Keep the
default margin when you annotate.
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.