nanoraster
0.4.1

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.

Open Markdown

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.

FormatAlphaLossyUse for
pngYesNoExact pixels, diffing, transparent thumbnails
webpYesOnly when quality is below 1Web delivery; lossless by default, optionally lossy
jpegNoYesInterop with tools that accept nothing else
jpgNoYesAlias for jpeg; the filename keeps the jpg extension
rawYesNoThe 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; quality sets the compression level and defaults to 0.92.
  • WebP defaults to 1, which encodes lossless. Any value below 1 switches to the lossy codec, which keeps alpha but never reaches exact pixels; this matches Chrome's canvas toBlob('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.

On this page