nanoraster
0.7.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

Every export on this page comes from nanoraster. The call owns the GLB bytes that you pass in. Returned bytes are newly allocated, and you own them.

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
  lineWidth: 3, // output pixels; default
  surfaces: true, // default
  lines: true, // default
  visiblePrimitives: undefined, // default (all reachable primitives)
  sections: undefined, // default (no section view)
  world: undefined, // default (glTF +Y up, +Z forward, metres)
  background: undefined, // default (transparent)
  axes: false, // default
  scaleBar: false, // default
  lighting: 'studio', // default
  label: undefined, // default (no label drawn)
  camera: undefined, // default fitted three-quarter perspective camera
});

renderImages accepts the same shared keys, except label and the singular camera. It adds views: [{ id, label?, camera?, width?, height?, format?, quality? }], one entry per image in result order. It accepts timings: true to attach stage timings.

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. This is the plan call. Each view can override the shared width, height, format and quality.

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 that preserves the input view IDs and order. Each entry's MIME type follows its own format override.
  • Throws: the same errors as renderImage, plus an error when the requested views and the returned images do not match.
import { renderImages } from 'nanoraster';

const [iso, front] = await renderImages(glb, {
  format: 'webp',
  views: [{ id: 'iso' }, { id: 'front', camera: { framing: 'fit', direction: [1, 0, 0] } }],
});

Encode pixels

encodeRgbaWebp

Encode tightly packed RGBA8 pixels as WebP without creating a GPU renderer.

export declare const encodeRgbaWebp: (
  rgba: Uint8Array<ArrayBuffer>,
  options: EncodeRgbaWebpOptions,
) => Promise<Uint8Array<ArrayBuffer>>;

The input is top-row-first sRGB. Set alpha to describe whether its RGB channels are straight or premultiplied; the encoder snapshots the input before starting asynchronous work.

EncodeRgbaWebpOptions

Renderer reuse

The one-shot functions share one renderer per process. The first call creates it, and nothing disposes it. Create your own renderer to control 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

createRenderer returns this persistent handle. Its two render methods mirror the module-level functions exactly: same options, same results, and byte-identical pixels on the same adapter.

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. Send a declared set of images through one renderImages call, not a loop.
  • The next call recovers device loss: it rebuilds the device and uploads again.
  • dispose() marks the renderer disposed at once. It is idempotent and backs using through Symbol.dispose. Every later call rejects with RenderError code 'gpu', so create a new renderer instead of retrying. GPU teardown follows the queued calls, with no completion signal.

CreateRendererOptions

describeAdapter

Describe the adapter that a renderer with these options would bind. Pass the powerPreference that you would pass to createRenderer.

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

deviceType === 'cpu' means software rasterisation, an order of magnitude slower. Use it as the preflight when performance matters. Native hosts report the full device class. Browsers report 'unknown' unless the adapter declares itself a fallback. When no adapter exists, the call 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' }],
} as const satisfies RenderImagesOptions;

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

satisfies RenderImageOptions does the same for the singular call.

Options

Frame the model and Place the camera show these fields in use.

RenderVector3

export type RenderVector3 = readonly [x: number, y: number, z: number];

A Cartesian vector in the request's caller-world frame, which is glTF world space when the request omits world. Direction and up vectors accept any finite non-zero magnitude, and the renderer normalizes them.

RenderWorldAxis

export type RenderWorldAxis = '+x' | '-x' | '+y' | '-y' | '+z' | '-z';

One signed principal axis in the caller-world frame.

RenderWorld

export type RenderWorld = {
  readonly up?: RenderWorldAxis;
  readonly forward?: RenderWorldAxis;
  readonly unit?: 'meter' | 'millimeter';
};

Declares the coordinate frame for camera, section, world-light, axes, and scale-bar values. It does not change the submitted GLB. The defaults are glTF +Y up, +Z forward, and metres. Up and forward must name distinct axes and form a right-handed frame. Every view in a batch shares the setting.

RenderPrimitiveReference

export type RenderPrimitiveReference = {
  readonly nodeIndex: number;
  readonly meshIndex: number;
  readonly primitiveIndex: number;
};

One reachable primitive instance in the submitted glTF document. All three indices are non-negative safe integers. See Choose visible geometry.

RenderSectionPlane

export type RenderSectionPlane = {
  readonly point: RenderVector3;
  readonly normal: RenderVector3;
};

The normal points into the retained world-space half-space. The renderer normalizes it.

RenderSections

export type RenderSections = {
  readonly planes: readonly [RenderSectionPlane, ...RenderSectionPlane[]];
  readonly clipSurfaces?: boolean;
  readonly clipLines?: boolean;
};

The request shares one to renderImageMaxSections planes. Both clip flags default to true. Cut section views covers caps, outlines, and multiple retained half-spaces. A successful closed-surface cut contains every cap. A failed cut returns parse.

RenderCamera

The tagged camera contract. A fit camera supplies orientation, and nanoraster solves a geometry-tight optical target, distance, and clipping. A fixed camera preserves the caller-owned pose and projection. Perspective is rectilinear and preserves the requested field of view exactly.

export type RenderCamera =
  | {
      readonly framing: 'fit';
      readonly direction?: RenderVector3;
      readonly up?: RenderVector3;
      readonly margin?: number;
      readonly projection?:
        | { readonly kind: 'perspective'; readonly verticalFieldOfView?: number }
        | { readonly kind: 'orthographic' };
    }
  | {
      readonly framing: 'fixed';
      readonly position: RenderVector3;
      readonly target: RenderVector3;
      readonly up: RenderVector3;
      readonly projection?:
        | { readonly kind: 'perspective'; readonly verticalFieldOfView?: number; readonly zoom?: number }
        | { readonly kind: 'orthographic'; readonly verticalSpan: number; readonly zoom?: number };
      readonly clipping?: { readonly near: number; readonly far: number };
    };

The default fit camera looks from 45 degrees azimuth and 30 degrees elevation in the declared world, and its up is world.up. In glTF world, that direction is [0.6123724357, 0.5, 0.6123724357] and that up is [0, 1, 0]. The minimum margin defaults to 0.1, the perspective field of view to 45, and the zoom to 1. direction fixes the orientation from the subject toward the camera. The fit can translate the optical axis to balance the projected geometry.

A fixed camera requires a position and target that do not coincide. A view collinear with up names no roll. Screen-up then becomes world.forward, or world.up along forward. Omitted clipping derives safe planes from referenced geometry. The renderer tightens an explicit range outside the referenced subject, without changing the visible geometry.

RenderOrbit

export type RenderOrbit = {
  readonly elevation: number;
  readonly azimuth: number;
};

A viewing direction as two angles in degrees around the caller world. elevation is the angle above the world horizontal plane, from -90 to 90. azimuth is the angle in that plane, zero on world.forward and positive toward the viewer's right.

directionFromOrbit

export declare const directionFromOrbit: (orbit: RenderOrbit, world?: RenderWorld) => RenderVector3;
  • Parameters: the orbit angles, and the world they are measured in. Pass the request's world, or omit it for the glTF default.
  • Returns: the unit direction that a fitted camera takes.
  • Throws: TypeError for a non-finite azimuth, an elevation outside -90 to 90, or an invalid world.
import { directionFromOrbit } from 'nanoraster';

const world = { up: '+z', forward: '-y' } as const;
const direction = directionFromOrbit({ azimuth: 45, elevation: 30 }, world);

orbitFromDirection

export declare const orbitFromDirection: (direction: RenderVector3, world?: RenderWorld) => RenderOrbit;
  • Parameters: any finite non-zero direction, and the world it is expressed in. The function ignores the magnitude.
  • Returns: the orbit angles that reproduce the direction. The result exactly inverts directionFromOrbit in the same world. The azimuth is above -180 and up to 180. At the poles, elevation -90 or 90, the azimuth is undefined and reported as 0.
  • Throws: TypeError for a zero or non-finite direction, or an invalid world.

RenderImageOptions

export type RenderImageOptions = RenderImageSharedOptions & RenderCameraOptions;

RenderImageSharedOptions and RenderCameraOptions are internal composition types.

RenderImageView

export type RenderImageView<Id extends string = string> = {
  readonly id: Id;
  readonly visiblePrimitives?: readonly RenderPrimitiveReference[];
  readonly label?: string;
  readonly camera?: RenderCamera;
  readonly width?: number;
  readonly height?: number;
  readonly format?: 'png' | 'webp' | 'jpeg' | 'jpg' | 'raw';
  readonly quality?: number;
};

IDs must be unique within a request. visiblePrimitives draws and fits to this view's exact source instances; omit it to inherit the shared selection. An empty selection needs a fixed camera, and per-view selection with sections is unsupported. The four per-view output overrides default to the shared value, follow the same validation, and flow into that entry's filename and MIME type. A view also accepts format: 'raw'. An omitted camera means the default fitted camera.

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 has a label only when its own entry sets label. timings: true attaches a RenderTimings to the result. It does not change what the call renders.

RenderAmbientOcclusion

Pass ao: {} to add screen-space contact shadows to opaque surfaces. Omit it to skip the depth and full-screen passes. The optional fields tune the radius in output pixels, visibility exponent, and distance rejection. AO uses the same profile for every view in a batch and leaves authored material factors unchanged.

StrictRenderImagesOptions

The exact parameter type of renderImages and Renderer.renderImages. It narrows RenderImagesOptions so that extra keys, empty view tuples, and per-view settings at call level are compile errors. Callers pass plain RenderImagesOptions literals. Name it 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. Three requests render the same bytes: omit lighting, pass 'studio', or state the studio values in full. 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. It inherits every other studio value that it leaves out. Directions are view-space by default: +x right, +y up, +z toward the viewer. space: 'world' uses the declared caller world.

RenderLight

One directional light. direction points from the surface toward the light, and the renderer normalises it. color is linear RGB radiance, unitless. Each channel is within renderImageLightColorRange.

Results

RenderedImageFile

renderImage returns this type, and every batch entry carries it as file. width and height are the resolved request dimensions. With format: 'raw', bytes is the frame itself: straight-alpha sRGB RGBA8, width * height * 4 bytes, top row first. Its mimeType is then 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 from 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. It narrows each entry's MIME type through the view's format override, falling back to the shared format. Entry files are named render-<id>.<format>.

RenderedImagesResult

The type that renderImages resolves to. It is the RenderedImages tuple, plus a RenderTimings on timings when the options literal set timings: true. A read of timings without that request is a compile error.

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

const options = {
  format: 'webp',
  timings: true,
  views: [{ id: 'iso' }],
} 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

A plan call's result carries this type when the options set timings: true. The fields map onto the pipeline stages: parse, shared cap-build, source upload, then per-view rasterise, annotate and encode. Resource counters show which work the call acquired. presentationBuilds is 1 for one plan.

RenderViewTimings

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

RenderSectionSourceTimings

How one plane capped one source primitive it cuts, in RenderTimings sections. certified is false when the source was sliced directly; openChains counts the cut outlines that do not close. Cut section views explains both.

imageMimeTypes

The readonly runtime map from output format to 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 can 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 Error 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 and classifies any other thrown value. It 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 that the validator applies, as the exact runtime values. Import them instead of restating values, 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]minimum empty fractioncamera.margin
renderImageVerticalFieldOfViewRangereadonly [1, 179]degreesverticalFieldOfView
renderImageZoomRangereadonly [0.01, 100]magnificationzoom
renderImageLineWidthRangereadonly [0.25, 16]output pixelslineWidth
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
renderImageMaxSections8planes per requestsections.planes
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 bounds for both image dimensions.

renderImageMarginRange

Inclusive minimum fitted-margin bounds.

renderImageVerticalFieldOfViewRange

Inclusive vertical perspective field-of-view bounds.

renderImageZoomRange

Inclusive perspective and orthographic magnification bounds.

renderImageLineWidthRange

Inclusive authored-edge line-width bounds.

renderImageAnnotatedMinDimension

Minimum width and height when any annotation is enabled.

renderImageQualityRange

Inclusive encoder-quality bounds. JPEG treats the value as a compression level, with default 0.92. WebP defaults to 1, lossless, and treats any lower value as lossy. PNG and raw ignore the value.

renderImageBackgroundPattern

The hex clear-colour syntax. It does not cover normalised straight-alpha RGBA backgrounds.

renderImageLabelMaxLength

Maximum label length.

renderImageLabelPattern

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

renderImageViewIdPattern

The view identifier syntax. IDs become filenames, so the pattern restricts the first character.

renderImageMaxLights

The directional-light cap for one rig.

renderImageMaxSections

The simultaneous retained-half-space plane cap for one request.

renderImageLightColorRange

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

renderImageAmbientRange

Inclusive bounds for the rig's flat ambient multiplier.

renderImageExposureRange

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

On this page