nanoraster
0.4.1

API reference

Every public export of nanoraster — the two render calls, the renderer handle, the request and result types, the failure codes, and the validation constants.

Open Markdown

Everything below is imported from nanoraster. GLB bytes you pass in are owned by the call; the bytes you get back are newly allocated and yours.

Every option and its default

import { renderImage } from 'nanoraster';

const image = await renderImage(glb, {
  format: 'webp', // required: 'png' | 'webp' | 'jpeg' | 'jpg' | 'raw'
  width: 768, // default
  height: 432, // default
  quality: 1, // webp default (lossless); jpeg defaults to 0.92; png and raw ignore it
  margin: 0.1, // default
  up: 'y', // default
  projection: 'perspective', // default
  background: undefined, // default (transparent)
  axes: false, // default
  scaleBar: false, // default
  lighting: 'studio', // default
  label: undefined, // default (no label drawn)
  phi: 60, // default
  theta: -45, // default
});

renderImages takes the same shared keys, minus phi, theta and label, plus views: [{ id, label?, phi, theta, width?, height?, format?, quality? }], one entry per image in result order, and timings: true to attach stage timings. format: 'raw' skips the encoder and returns the frame itself, at call level or per view; see Work with raw pixels.

Render

renderImage

Render one GLB camera view to one owned output.

export declare const renderImage: (
  glb: Uint8Array<ArrayBuffer>,
  options: RenderImageOptions,
) => Promise<RenderedImageFile>;
  • Parameters: owned GLB bytes and one validated render request.
  • Returns: a newly allocated render.<format> file with the resolved width and height.
  • Throws: RenderError for invalid requests, malformed GLB data, GPU faults, or encoding failures.
import { renderImage } from 'nanoraster';

const image = await renderImage(glb, { format: 'webp', width: 512 });

renderImages

Parse and upload a GLB once, then render an ordered set of identified views: the plan call. Each view may override the shared width, height, format and quality, so one call renders a whole resolution or format ladder.

export declare const renderImages: <const Options extends RenderImagesOptions>(
  glb: Uint8Array<ArrayBuffer>,
  options: StrictRenderImagesOptions<Options>,
) => Promise<RenderedImagesResult<Options>>;
  • Parameters: owned GLB bytes and shared settings with a non-empty view tuple.
  • Returns: a tuple with the input view IDs and order preserved; each entry's MIME type follows its own format override.
  • Throws: as above, plus a mismatch between the views requested and the images returned.
import { renderImages } from 'nanoraster';

const [iso, front] = await renderImages(glb, {
  format: 'webp',
  views: [
    { id: 'iso', phi: 60, theta: -45 },
    { id: 'front', phi: 90, theta: 0 },
  ],
});

Renderer reuse

The one-shot functions share one lazily created renderer per process, run in sequence and never disposed; create your own to control its lifetime or power preference. Reuse the renderer covers when and how.

createRenderer

Create a renderer that keeps the GPU device, shader and pipelines alive across calls.

export declare const createRenderer: (options?: CreateRendererOptions) => Promise<Renderer>;
  • Parameters: optional GPU selection hints.
  • Returns: a Renderer bound to one warm device.
  • Throws: RenderError with code adapter-unavailable when no compatible adapter exists, or parse for invalid options.
import { createRenderer } from 'nanoraster';

using renderer = await createRenderer({ powerPreference: 'low-power' });
const image = await renderer.renderImage(glb, { format: 'webp' });

Renderer

The persistent handle returned by createRenderer. Its two render methods mirror the module-level functions exactly (same options, same results, byte-identical pixels on the same adapter) but reuse one device.

export type Renderer = {
  readonly renderImage: (
    glb: Uint8Array<ArrayBuffer>,
    options: RenderImageOptions,
  ) => Promise<RenderedImageFile>;
  readonly renderImages: <const Options extends RenderImagesOptions>(
    glb: Uint8Array<ArrayBuffer>,
    options: StrictRenderImagesOptions<Options>,
  ) => Promise<RenderedImagesResult<Options>>;
  readonly dispose: () => void;
  readonly [Symbol.dispose]: () => void;
};
  • A renderer is single-realm: create it inside the worker that uses it; bytes cross postMessage, handles do not.
  • Calls on one renderer run in sequence; a declared set of images goes through one renderImages call, never a loop.
  • Device loss is recovered on the next call, which rebuilds the device and re-uploads.
  • dispose() marks the renderer disposed at once, is idempotent, and backs using through Symbol.dispose; every later call rejects with RenderError code 'gpu', so recreate rather than retry. GPU teardown follows the queued calls, with no completion signal.

CreateRendererOptions

describeAdapter

Describe the adapter a renderer created with these options would bind; pass the same powerPreference you would pass createRenderer.

export declare const describeAdapter: (options?: CreateRendererOptions) => Promise<AdapterInfo | undefined>;

deviceType === 'cpu' means software rasterisation, an order of magnitude slower; use this as the preflight when performance matters. Native hosts report the full device class; browsers report 'unknown' unless the adapter declares itself a fallback. No adapter resolves undefined; only invalid options reject.

AdapterInfo

Options held in a variable

A variable widens 'webp' to string and the view IDs with it, so the result tuple loses its names; as const satisfies keeps the literals and still checks the keys, at compile time only.

import { renderImages, type RenderImagesOptions } from 'nanoraster';

const options = {
  format: 'webp',
  views: [{ id: 'iso', phi: 60, theta: -45 }],
} as const satisfies RenderImagesOptions;

const [iso] = await renderImages(glb, options);

satisfies RenderImageOptions does the same for the singular call.

Options

Frame the model shows these fields in use.

RenderImageOptions

export type RenderImageOptions = RenderImageSharedOptions &
  RenderLabelOptions & {
    readonly phi?: number;
    readonly theta?: number;
  };

RenderImageSharedOptions and RenderLabelOptions are internal composition types; the table is the complete consumer-visible shape.

RenderImageView

export type RenderImageView<Id extends string = string> = {
  readonly id: Id;
  readonly label?: string;
  readonly phi: number;
  readonly theta: number;
  readonly width?: number;
  readonly height?: number;
  readonly format?: 'png' | 'webp' | 'jpeg' | 'jpg' | 'raw';
  readonly quality?: number;
};

IDs must be unique within a request. The four output fields are per-view overrides: each defaults to the shared value, follows the same validation, and flows into that entry's filename and MIME type; format: 'raw' is accepted here too.

RenderImagesOptions

export type RenderImagesOptions<Views extends readonly RenderImageView[] = readonly RenderImageView[]> =
  RenderImageSharedOptions & {
    readonly timings?: boolean;
    readonly views: Views;
  };

views must be a non-empty ordered tuple with unique IDs; a view is labelled when its own entry sets label. timings: true attaches a RenderTimings to the result without changing what is rendered.

StrictRenderImagesOptions

The exact parameter type of renderImages and Renderer.renderImages: RenderImagesOptions narrowed so extra keys, empty view tuples, and per-view settings placed at call level are compile errors. Callers pass plain RenderImagesOptions literals; name this type only when you forward the plan call through a generic wrapper:

import { renderImages, type RenderImagesOptions, type StrictRenderImagesOptions } from 'nanoraster';

const renderCatalogue = async <const Options extends RenderImagesOptions>(
  glb: Uint8Array<ArrayBuffer>,
  options: StrictRenderImagesOptions<Options>,
) => renderImages(glb, options);

RenderLighting

export type RenderLighting = 'studio' | RenderLightingRig;

The studio preset by name, or a rig. Omitting lighting, passing 'studio' and spelling out the studio values render the same bytes; one rig applies to every view of a batch. Light the subject shows each field in use.

RenderLightingRig

A rig replaces the studio lights outright and inherits every other studio value it leaves out. Directions are view-space by default (+x right, +y up, +z toward the viewer); space: 'world' authors them in glTF coordinates.

RenderLight

One directional light. direction points from the surface toward the light and is normalised by the renderer; color is linear RGB radiance, unitless, each channel within renderImageLightColorRange.

Results

RenderedImageFile

Returned by renderImage, and as the file of every batch entry. width and height are the dimensions the request resolved to. With format: 'raw', bytes is the frame itself (straight-alpha sRGB RGBA8, width * height * 4 bytes, top row first) and mimeType is application/octet-stream; Work with raw pixels has the layout in full.

import { renderImage } from 'nanoraster';

import { writeFile } from 'node:fs/promises';

const image = await renderImage(glb, { format: 'webp' });
await writeFile(image.name, image.bytes);

RenderedImage

One identified entry in a batch result; the id is the literal you supplied on the matching view.

RenderedImages

The ordered tuple inside every plan-call result.

export type RenderedImages<
  Views extends readonly RenderImageView[],
  SharedFormat extends ImageFormat = ImageFormat,
> = {
  readonly [Index in keyof Views]: Views[Index] extends RenderImageView<infer Id>
    ? RenderedImage<Id, ViewOutputFormat<Views[Index], SharedFormat>>
    : never;
};

This mapped tuple preserves each input view's literal ID, position and length, and narrows each entry's MIME type through its view's format override, falling back to the shared format. Entry files are named render-<id>.<format>.

RenderedImagesResult

What renderImages resolves to: the RenderedImages tuple, plus a RenderTimings on timings when the options literal set timings: true. Reading timings without having requested it is a compile error.

import { renderImages, type RenderedImagesResult, type RenderImagesOptions } from 'nanoraster';

const options = {
  format: 'webp',
  timings: true,
  views: [{ id: 'iso', phi: 60, theta: -45 }],
} as const satisfies RenderImagesOptions;

const result: RenderedImagesResult<typeof options> = await renderImages(glb, options);
console.log(result[0].file.name, result.timings.parse); // 'render-iso.webp' 0.42

RenderTimings

Attached to a plan call's result when the options set timings: true. The fields map onto the pipeline stages: parse, setup (device acquisition plus geometry upload), then per-view rasterise, annotate and encode, all in milliseconds.

RenderViewTimings

One view's stage timings within a RenderTimings, in plan order.

imageMimeTypes

The readonly runtime map of every accepted output format to its emitted MIME type.

export declare const imageMimeTypes: {
  readonly png: 'image/png';
  readonly webp: 'image/webp';
  readonly jpeg: 'image/jpeg';
  readonly jpg: 'image/jpeg';
  readonly raw: 'application/octet-stream';
};

Errors

Rendering rejects with RenderError. Parse and encode failures are deterministic for the same request; GPU faults may succeed after the host recovers, so Handle render failures retries them.

RenderFailureCode

export type RenderFailureCode =
  | 'adapter-unavailable'
  | 'device-lost'
  | 'driver-unsupported'
  | 'gpu'
  | 'parse'
  | 'encode'
  | 'unknown';
ValueMeaningRetry guidance
adapter-unavailableNo compatible GPU adapter was available.Keep the last image and retry after host recovery.
device-lostThe GPU device was lost during rendering.Keep the last image and retry with a new device.
driver-unsupportedThe host's GPU driver faults mid-render on 32-bit ARM Linux.Render on another driver, or set the documented opt-out.
gpuAnother adapter, driver, or render-pass failure occurred.Treat as transient unless host diagnostics prove otherwise.
parseGLB bytes or request options violated the input contract.Fix the input before retrying.
encodeThe selected encoder could not produce the requested image.Fix the format or background before retrying.
unknownThe thrown value had no recognised failure tag.Inspect message and preserve the original context.

RenderError

RenderError extends the native Error class and adds this public surface:

export declare class RenderError extends Error {
  readonly code: RenderFailureCode;
  constructor(code: RenderFailureCode, message: string);
  static from(error: unknown): RenderError;
  get isGpuFault(): boolean;
}

RenderError.from(error) returns an existing RenderError unchanged or classifies any other thrown value, and never throws. isGpuFault is true for adapter-unavailable, device-lost and gpu.

import { RenderError, renderImage } from 'nanoraster';

try {
  await renderImage(glb, { format: 'webp' });
} catch (error) {
  const failure = RenderError.from(error);
  if (!failure.isGpuFault) throw failure;
}

Constants

Every bound and pattern the validator applies, as the exact runtime values; import them rather than restating them, so caller-side checks cannot drift.

ExportRuntime valueUnit or syntaxAffected field
renderImageDimensionRangereadonly [16, 4096]pixelswidth, height
renderImageQualityRangereadonly [0, 1]normalised encoder qualityquality
renderImageMarginRangereadonly [0, 0.5]fraction of fitted extentmargin
renderImageAnnotatedMinDimension192pixelsannotated width, height
renderImageLabelMaxLength64Unicode code pointslabel
renderImageLabelPattern/^[\u0020-\u007E\u00B5\u2014\u2212]+$/usupported characterslabel
renderImageViewIdPattern/^[\dA-Za-z][\w-]{0,63}$/uidentifier syntaxid
renderImageBackgroundPattern/^#[\dA-Fa-f]{6}(?:[\dA-Fa-f]{2})?$/uhexadecimal colourbackground
renderImageMaxLights8lights per riglighting.lights
renderImageLightColorRangereadonly [0, 32]linear radiance per channellighting.lights[].color
renderImageAmbientRangereadonly [0, 4]diffuse multiplierlighting.ambient
renderImageExposureRangereadonly [0.01, 16]linear multiplierlighting.exposure
import { renderImageDimensionRange } from 'nanoraster';

const [min, max] = renderImageDimensionRange;

const clamp = (value: number): number => Math.min(Math.max(value, min), max);

renderImageDimensionRange

Inclusive pixel bounds for both image dimensions.

renderImageMarginRange

Inclusive corner-fit margin bounds.

renderImageAnnotatedMinDimension

Minimum width and height when any annotation is enabled.

renderImageQualityRange

Inclusive encoder-quality bounds. JPEG treats the value as compression level (default 0.92); WebP treats 1, its default, as lossless and anything lower as lossy; PNG and raw ignore it.

renderImageBackgroundPattern

Hex clear-colour syntax; normalised straight-alpha RGBA backgrounds are not covered by it.

renderImageLabelMaxLength

Maximum label length in Unicode code points.

renderImageLabelPattern

Supported label characters: printable ASCII plus the micro sign, em dash, and minus sign.

renderImageViewIdPattern

Stable view identifier syntax; IDs become result filenames, so the first character is restricted.

renderImageMaxLights

Most directional lights one rig may carry.

renderImageLightColorRange

Inclusive per-channel bounds for a light's linear radiance.

renderImageAmbientRange

Inclusive bounds for the rig's flat ambient multiplier.

renderImageExposureRange

Inclusive bounds for the pre-tone-map exposure multiplier.

On this page