Session.capture ​

Contract 2.0.0 · package sha256 a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e · generated from configurator-runtime.d.ts by services/onsite-scripter/scripts/build-contract-docs.mjs; edit the source, not this file.

Method of Session · inherited by testing.FakeSession · stable · since 2.0.0 · called as session.capture

ts
capture(spec?: CaptureSpec, options?: SignalOption): Promise<CaptureResult>;

Captures committed state as an image. Read class: observed. Never joins or waits for a transaction. With no hold, waits for an autocommit write's running beforeCommit functions to finish, then for readiness and for the committed state's pending late swaps (detail maps landing after the colour maps), together ≤ readyTimeoutMs, then renders off-display: the canvas and the live camera are untouched. To copy the frame on screen instead, pass source: 'displayed': it copies the next frame the view presents, so on a hidden page or in a cross-origin iframe scrolled out of view, which draw no frame, it resolves only once the view draws again; the default source renders at once. Open-ended motion doesn't delay it. A write called but not yet committed (a pick, a store sync or a rule still loading) is not in the image: for the configuration the shopper just made, wait with idle() first (the example; spec section 5.11). A capture started before destroy() still resolves, even one called in the same task: it draws the committed state at destroy(), once its code is in (that code arrives at idle after the first complete frame; code still loading at destroy() gets readyTimeoutMs, after which the capture rejects AbortError). Aborting signal rejects AbortError; nothing rolls back. Errors: while a transaction holds the display or runs its beforeCommit functions, rejects at once with HoloBusyError('capture-held'). To wait: await session.idle({ signal }), then capture again. idle() waits for that transaction to settle: when it is your own and its work waits on you, finish or abort it first, or it rolls back with reason 'timeout' after its timeoutMs. A hold that starts while it waits rejects it then, with the same error; it renders the newest committed state when it draws. Rejects HoloInputError ('capture-displayed-fixed') for source: 'displayed' with a view, size, pixelRatio or non-'scene' background, also during a hold. Throws HoloScopeError inside a beforeCommit function: it would wait for that function's own commit (spec section 6.1, O3); capture on 'committed' or after the write's Promise. If the frame draws but the image encoder gives no Blob, rejects DOMException 'EncodingError': no CaptureResult, nothing in the scene or history changes, and the capture's resources are released. Not HoloBusyError: waiting with idle() doesn't help, so omit the image or capture a smaller one. Not for: moving the live camera to take a picture; pass spec.view.

Default: spec {} (the live camera, the element's size, the live view's pixel ratio, png)

Options ​

Option Type Default What it does
signal AbortSignal undefined Cancels this call; the member's JSDoc says what an abort does.

Abort (signal) ​

This call is a wait (SignalOption.signal, the kinds of call).

A wait (idle, capture): aborting rejects AbortError and nothing rolls back.

Example ​

Imports: values (has, validate, the Holo*Error classes, the pure helpers) from @holostep-configurators/runtime-client, types from @holostep-configurators/runtime-client/contract, /math and /testing names from those entries; boot from the runtime module the root's loadRuntimeAtFloor resolves to. A type your page defines is your app's: declare it there, never import it from the package.

Compiled under the contract's strict settings (examples/declaration/05-Session.capture.ts):

ts
import { HoloBusyError } from '@holostep-configurators/runtime-client';
import type { CaptureResult, CaptureSpec, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
declare function upload(image: Blob, sceneHash: string): Promise<void>;
const spec: CaptureSpec = {
  view: { azimuthDeg: 45, polarDeg: 70, distance: 2.4, target: { x: 0, y: 0.4, z: 0 } },
  // exactly 800 × 800 pixels
  size: { width: 800, height: 800 }, pixelRatio: 1, background: 'transparent',
};
async function thumbnail(): Promise<CaptureResult> {
  try {
    return await session.capture(spec, { signal });
  } catch (error) {
    // 'capture-held': a transaction holds the display
    if (!(error instanceof HoloBusyError)) throw error;
    await session.idle({ signal });                        // bounded by the signal
    return session.capture(spec, { signal });
  }
}
// a pick made just before the tap may still be loading: wait for it, at most 5 s
try {
  await session.idle({ signal: AbortSignal.timeout(5000) });
} catch (error) {
  if (!(error instanceof DOMException && error.name === 'AbortError')) throw error;
  // timed out: the image may still show the previous pick
}
const shot = await thumbnail();
await upload(shot.image, shot.snapshot.sceneHash);

Generic-API test (spec 3.1) ​

Rule Result
1. Neutral name pass: capture names no product
2. A precedent pass: CSS saturate() (row G17); HTMLCanvasElement.toBlob (row G19); Unity Camera.Render (to a RenderTexture) (row G20); Unity ScreenCapture.CaptureScreenshot (row K38); a correctness rule (rows G21)
3. Two apps, or labs pass: 27 apps (ledger: app-04c2774145, app-0c773a4ee3, app-37356bf09f, app-4762caacdc, app-4f16d2f6da, app-4fd23bc869, app-510d750508, app-59c5b5751e and 19 more; fleet cross-check: app-04c2774145, app-4762caacdc, app-4f16d2f6da, app-4fd23bc869, app-510d750508, app-822bb9d6aa, app-8675b3225c, app-b362a81c85 and 16 more)
4. The cheaper fix first decided per need in Appendix B: rows G17, G19, G20, G21, K38 at tier stable (a member only where no rule, data, asset or app code serves the need)