Render a view sheet
Build a four-view contact sheet from one GLB, learning batch rendering, fitted cameras, and labels along the way.
Build a labelled four-view contact sheet (front, right, top, and iso) from one GLB in a single render call.
Step 1: Render a single fitted view
Start with one view, so the camera controls are visible on their own.
Frame the model explains direction and up.
import { directionFromOrbit, renderImage } from 'nanoraster';import { readFile, writeFile } from 'node:fs/promises';const glb = Uint8Array.from(await readFile('model.glb'));const front = await renderImage(glb, { format: 'webp', width: 512, height: 512, camera: { framing: 'fit', direction: directionFromOrbit({ azimuth: 90, elevation: 0 }), },});await writeFile('front.webp', front.bytes);Rendering…
advanced · direction XYZ
Verify: front.webp shows the gear face-on, teeth all around the rim.
Step 2: Switch to the batch call
A separate call for each view parses and uploads the GLB every time.
renderImages does that once, then renders every view in the list. Start with
two views. Add the rest in step 3. A sheet needs a tighter fit than a single
hero image, so each preset uses margin: 0.05.
import { directionFromOrbit, renderImages } from 'nanoraster';import { readFile, writeFile } from 'node:fs/promises';const glb = Uint8Array.from(await readFile('model.glb'));const sheet = await renderImages(glb, { format: 'webp', width: 512, height: 512, views: [ { id: 'front', camera: { framing: 'fit', direction: directionFromOrbit({ azimuth: 90, elevation: 0 }), margin: 0.05, }, }, { id: 'right', camera: { framing: 'fit', direction: directionFromOrbit({ azimuth: 180, elevation: 0 }), margin: 0.05, }, }, ],});for (const { id, file } of sheet) { await writeFile(file.name, file.bytes); console.log(id, file.name);}Rendering…
advanced · direction XYZ
Verify: two files appear. The console prints the IDs in the order you declared them:
front render-front.webp
right render-right.webpThe result filename carries the view id, not a counter.
Step 3: Add the top and iso views
The top view looks down the up axis. The iso view is the default three-quarter
angle. axes draws a camera-aware marker in each image. This marker orients
the top view once the image is on its own.
const sheet = await renderImages(glb, { format: 'webp', width: 512, height: 512, axes: true, views: [ { id: 'front', camera: { framing: 'fit', direction: directionFromOrbit({ azimuth: 90, elevation: 0 }), margin: 0.05, }, }, { id: 'right', camera: { framing: 'fit', direction: directionFromOrbit({ azimuth: 180, elevation: 0 }), margin: 0.05, }, }, { id: 'top', camera: { framing: 'fit', direction: directionFromOrbit({ azimuth: 0, elevation: 90 }), up: [0, 0, 1], margin: 0.05, }, }, { id: 'iso', camera: { framing: 'fit', direction: directionFromOrbit({ azimuth: 45, elevation: 30 }), margin: 0.05, }, }, ],});Rendering…
advanced · direction XYZ
Verify: four files appear. render-top.webp looks down at the model.
render-iso.webp uses the quick start's direction.
Step 4: Label each view
The renderer draws each label into the image, screen-upright. A sheet then
stays readable once the images are separated from their filenames. A view
carries a label when its entry sets label. There is no flag to set. Clear a
view's label entry to remove its label. scaleBar adds the bottom-left
bar, so the finished sheet also carries its own units.
const sheet = await renderImages(glb, { format: 'webp', width: 512, height: 512, axes: true, scaleBar: true, views: [ { id: 'front', camera: { framing: 'fit', direction: directionFromOrbit({ azimuth: 90, elevation: 0 }), margin: 0.05, }, label: 'Front', }, { id: 'right', camera: { framing: 'fit', direction: directionFromOrbit({ azimuth: 180, elevation: 0 }), margin: 0.05, }, label: 'Right', }, { id: 'top', camera: { framing: 'fit', direction: directionFromOrbit({ azimuth: 0, elevation: 90 }), up: [0, 0, 1], margin: 0.05, }, label: 'Top', }, { id: 'iso', camera: { framing: 'fit', direction: directionFromOrbit({ azimuth: 45, elevation: 30 }), margin: 0.05, }, label: 'Iso', }, ],});Rendering…
advanced · direction XYZ
Verify: each image carries its name in the top-left corner.
Step 5: Keep it reproducible
Run the script twice. Compare the files. The sheet regenerates byte-for-byte on the same host. How it works states what is and is not guaranteed across hosts.