Prismalpha.9
Runtime

paintOnce()

Wait for one Prism-owned paint pass.

paintOnce() waits for a Prism-owned paint pass. It does not export image data.

await document.fonts.ready;
await runtime.paintOnce();

const blob = await new Promise<Blob | null>((resolve) => {
  canvas.toBlob(resolve, "image/png");
});

Use it when you need a deterministic readiness point before calling normal canvas APIs such as toBlob() or toDataURL().

paintOnce() works without starting the runtime loop.

Multiple pending paintOnce() calls resolve from the same completed paint pass.

If paintOnce() is called during onPaint(), it waits for the next paint pass. It does not resolve from the pass already in progress.

Pending paintOnce() waiters reject if a paint handler throws or the runtime is destroyed.

stop() stops animation but does not cancel pending one-shot paints. Native paints can reject with PrismNativeError code paint-timeout after their visible-time budget expires. Hidden time pauses that budget without resetting it, and paints completed while hidden still resolve. A late event cannot revive rejected callers. Timers and visibility listeners are removed when the request settles or the runtime is destroyed. nativePaintTimeoutMs defaults provisionally to 2000 ms; this is a diagnostic threshold, not a scheduling guarantee.

Only pending paintOnce() callers own this deadline. invalidate() without a waiting caller does not time out; a caller joining its outstanding native request starts the budget then. Further invalidation or coalesced callers do not reset it.

Catching a native drawSurface() failure inside onPaint() cannot turn that pass into success. Its waiters still reject with the original PrismNativeError, and native diagnostics retain the failure. A later successful pass can restore health.