# `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`](Session.md) · inherited by [`testing.FakeSession`](testing.FakeSession.md) · 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`](Session.beforeCommit.md) 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()`](Session.idle.md) first (the example; spec section 5.11).
A capture started before [`destroy()`](Session.destroy.md) still resolves, even one called in the same task: it
draws the committed state at [`destroy()`](Session.destroy.md), once its code is in (that code arrives at idle after
the first complete frame; code still loading at [`destroy()`](Session.destroy.md) 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`](Session.beforeCommit.md) functions, rejects at
once with HoloBusyError('capture-held'). To wait: `await session.idle({ signal })`, then capture again.
[`idle()`](Session.idle.md) 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`](Session.beforeCommit.md) 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()`](Session.idle.md) 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](../read-this-first.md#conventions)).

> 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()](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/saturate) (row `G17`); [HTMLCanvasElement.toBlob](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) (row `G19`); [Unity Camera.Render (to a RenderTexture)](https://docs.unity3d.com/ScriptReference/Camera.Render.html) (row `G20`); [Unity ScreenCapture.CaptureScreenshot](https://docs.unity3d.com/ScriptReference/ScreenCapture.CaptureScreenshot.html) (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) |
