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.
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:
RenderErrorfor 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
formatoverride. - 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
Rendererbound to one warm device. - Throws:
RenderErrorwith codeadapter-unavailablewhen no compatible adapter exists, orparsefor 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
renderImagescall, 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 backsusingthroughSymbol.dispose. Every later call rejects withRenderErrorcode'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
directionthat a fitted camera takes. - Throws:
TypeErrorfor a non-finite azimuth, an elevation outside-90to90, 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
directionFromOrbitin the same world. The azimuth is above-180and up to180. At the poles, elevation-90or90, the azimuth is undefined and reported as0. - Throws:
TypeErrorfor 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.42RenderTimings
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';| Value | Meaning | Retry guidance |
|---|---|---|
adapter-unavailable | No compatible GPU adapter was available. | Keep the last image and retry after host recovery. |
device-lost | The GPU device was lost during rendering. | Keep the last image and retry with a new device. |
driver-unsupported | The host's GPU driver faults mid-render on 32-bit ARM Linux. | Render on another driver, or set the documented opt-out. |
gpu | Another adapter, driver, or render-pass failure occurred. | Treat as transient unless host diagnostics prove otherwise. |
parse | GLB bytes or request options violated the input contract. | Fix the input before retrying. |
encode | The selected encoder could not produce the requested image. | Fix the format or background before retrying. |
unknown | The 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.
| Export | Runtime value | Unit or syntax | Affected field |
|---|---|---|---|
renderImageDimensionRange | readonly [16, 4096] | pixels | width, height |
renderImageQualityRange | readonly [0, 1] | normalised encoder quality | quality |
renderImageMarginRange | readonly [0, 0.5] | minimum empty fraction | camera.margin |
renderImageVerticalFieldOfViewRange | readonly [1, 179] | degrees | verticalFieldOfView |
renderImageZoomRange | readonly [0.01, 100] | magnification | zoom |
renderImageLineWidthRange | readonly [0.25, 16] | output pixels | lineWidth |
renderImageAnnotatedMinDimension | 192 | pixels | annotated width, height |
renderImageLabelMaxLength | 64 | Unicode code points | label |
renderImageLabelPattern | /^[\u0020-\u007E\u00B5\u2014\u2212]+$/u | supported characters | label |
renderImageViewIdPattern | /^[\dA-Za-z][\w-]{0,63}$/u | identifier syntax | id |
renderImageBackgroundPattern | /^#[\dA-Fa-f]{6}(?:[\dA-Fa-f]{2})?$/u | hexadecimal colour | background |
renderImageMaxLights | 8 | lights per rig | lighting.lights |
renderImageMaxSections | 8 | planes per request | sections.planes |
renderImageLightColorRange | readonly [0, 32] | linear radiance per channel | lighting.lights[].color |
renderImageAmbientRange | readonly [0, 4] | diffuse multiplier | lighting.ambient |
renderImageExposureRange | readonly [0.01, 16] | linear multiplier | lighting.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.