Vize

Webcam Capture

Headless camera capture: preview, device switching, countdown shutter, and photos.

Package @vizejs/ui/webcam-capture
Maturity stable
Own the source vize lib pull webcam-capture
Requires context, controllable-state, id
Aliases webcam, camera capture, photo booth, selfie camera, getUserMedia
Covers MediaDevices.getUserMedia, HTMLCanvasElement.toBlob, @vizejs/composable useUserMedia, react-webcam

Usage

import { WebcamCaptureDeviceSelect, WebcamCapturePhoto, WebcamCapture, WebcamCaptureRoot, WebcamCaptureShutter, WebcamCaptureStartButton, WebcamCaptureStatusMessage, WebcamCaptureStopButton, WebcamCaptureSwitchCamera, WebcamCaptureVideo } from "@vizejs/ui/webcam-capture";

Or copy the source into your project with vize lib pull webcam-capture (see Source Distribution).

API

WebcamCaptureDeviceSelect

Source: src/families/media/webcam-capture/webcam-capture-device-select.vue

Props

Prop Type Default Description
ariaLabel string undefined Accessible name. Defaults to messages.deviceSelect.

Slots

Slot Slot props Description
option { readonly device: WebcamCaptureDevice; readonly selected: boolean } Option content per camera. Defaults to the device label.

Exposed

Member Type Description
element HTMLSelectElement | null Rendered native select.

WebcamCapturePhoto

Source: src/families/media/webcam-capture/webcam-capture-photo.vue

Props

Prop Type Default Description
alt string undefined Alternative text. Defaults to messages.photo.

Slots

Slot Slot props Description
empty Record<string, never> Rendered instead of nothing while no photo exists, e.g. a placeholder.
default { readonly photo: WebcamCapturePhotoResult; readonly url: string } Extra content after the image, e.g. a download link. Receives the photo and its URL.

Exposed

Member Type Description
element HTMLImageElement | null Rendered image, or null without a photo.

WebcamCapture

Source: src/families/media/webcam-capture/webcam-capture-root.vue

Props

Prop Type Default Description
id string | null undefined Consumer-owned root id. null and undefined select a deterministic fallback.
stream WebcamMediaStreamLike | null undefined Consumer-owned stream, e.g. useUserMedia().stream.value. Any value other than undefined (including null) selects external mode: the root never acquires or stops tracks itself.
constraints MediaTrackConstraints undefined Extra video track constraints for owned acquisition (resolution, frame rate…).
audio boolean false Also request the microphone for owned acquisition.
facingMode WebcamFacingMode undefined Controlled facing direction (v-model:facingMode). undefined selects uncontrolled use.
defaultFacingMode WebcamFacingMode "user" Initial facing direction for uncontrolled use.
deviceId string | null undefined Controlled camera (v-model:deviceId). null clears the selection; undefined selects uncontrolled use.
defaultDeviceId string undefined Initial camera for uncontrolled use.
autoStart boolean false Request the camera on mount. Otherwise permission is only requested by start().
mirrored boolean undefined Mirror the preview and captures. undefined mirrors the user camera only.
host WebcamMediaHost | null navigator.mediaDevices MediaDevices capability for alternate runtimes and tests.
captureType WebcamCaptureImageType "image/png" Encoded capture type.
captureQuality number undefined Encoder quality for lossy capture types, from 0 to 1.
aspectRatio number undefined Center-crop captures to this width / height ratio.
maxWidth number undefined Downscale captures to at most this width in pixels.
countdownInterval number 1000 Milliseconds per shutter countdown step.
messages Partial<WebcamCaptureMessages> undefined Localized labels and announcements. Omitted entries use English defaults.

Events

Event Payload Description
update:facingMode [facingMode: WebcamFacingMode] Fired when the facing direction requests a new controlled value.
update:deviceId [deviceId: string | null] Fired when the selected camera requests a new controlled value.
statusChange [status: WebcamCaptureStatus, previous: WebcamCaptureStatus] Fired after every distinct status transition.
error [failure: WebcamCaptureFailure] Fired when owned acquisition fails.
streamChange [stream: WebcamMediaStreamLike | undefined] Fired when an owned stream starts or stops.
capture [photo: WebcamCapturePhotoResult] Fired after a photo is captured.
captureError [error: unknown] Fired when a capture fails to draw or encode.

Slots

Slot Slot props Description
default WebcamCaptureSlotState Video, controls, photo, and status parts. Receives the camera state.

Exposed

Member Type Description
capture —
capturing —
clearPhoto —
countdown —
deviceId —
devices —
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.
error —
external —
facingMode —
mirrored —
photo —
photoUrl —
selectDevice —
start —
status —
stop —
stream —
switchCamera —

WebcamCaptureRoot

Source: src/families/media/webcam-capture/webcam-capture-root.vue

Props

Prop Type Default Description
id string | null undefined Consumer-owned root id. null and undefined select a deterministic fallback.
stream WebcamMediaStreamLike | null undefined Consumer-owned stream, e.g. useUserMedia().stream.value. Any value other than undefined (including null) selects external mode: the root never acquires or stops tracks itself.
constraints MediaTrackConstraints undefined Extra video track constraints for owned acquisition (resolution, frame rate…).
audio boolean false Also request the microphone for owned acquisition.
facingMode WebcamFacingMode undefined Controlled facing direction (v-model:facingMode). undefined selects uncontrolled use.
defaultFacingMode WebcamFacingMode "user" Initial facing direction for uncontrolled use.
deviceId string | null undefined Controlled camera (v-model:deviceId). null clears the selection; undefined selects uncontrolled use.
defaultDeviceId string undefined Initial camera for uncontrolled use.
autoStart boolean false Request the camera on mount. Otherwise permission is only requested by start().
mirrored boolean undefined Mirror the preview and captures. undefined mirrors the user camera only.
host WebcamMediaHost | null navigator.mediaDevices MediaDevices capability for alternate runtimes and tests.
captureType WebcamCaptureImageType "image/png" Encoded capture type.
captureQuality number undefined Encoder quality for lossy capture types, from 0 to 1.
aspectRatio number undefined Center-crop captures to this width / height ratio.
maxWidth number undefined Downscale captures to at most this width in pixels.
countdownInterval number 1000 Milliseconds per shutter countdown step.
messages Partial<WebcamCaptureMessages> undefined Localized labels and announcements. Omitted entries use English defaults.

Events

Event Payload Description
update:facingMode [facingMode: WebcamFacingMode] Fired when the facing direction requests a new controlled value.
update:deviceId [deviceId: string | null] Fired when the selected camera requests a new controlled value.
statusChange [status: WebcamCaptureStatus, previous: WebcamCaptureStatus] Fired after every distinct status transition.
error [failure: WebcamCaptureFailure] Fired when owned acquisition fails.
streamChange [stream: WebcamMediaStreamLike | undefined] Fired when an owned stream starts or stops.
capture [photo: WebcamCapturePhotoResult] Fired after a photo is captured.
captureError [error: unknown] Fired when a capture fails to draw or encode.

Slots

Slot Slot props Description
default WebcamCaptureSlotState Video, controls, photo, and status parts. Receives the camera state.

Exposed

Member Type Description
status WebcamCaptureStatus Stream lifecycle.
error WebcamCaptureFailure | undefined Last acquisition failure, or undefined.
external boolean Whether the stream is supplied by the consumer through the stream prop.
facingMode WebcamFacingMode Requested facing direction.
deviceId string | undefined Selected device id, or undefined for the facing-mode default.
devices readonly WebcamCaptureDevice[] Known cameras (filled after permission is granted).
mirrored boolean Whether the preview and captures are mirrored.
countdown number Seconds left in a running shutter countdown, or 0.
capturing boolean Whether a capture is being encoded.
element HTMLDivElement | null Rendered root element.
stream WebcamMediaStreamLike | undefined Current stream (owned or external), or undefined.
photo WebcamCapturePhotoResult | null Last captured photo, or null.
photoUrl string | undefined Object URL of the last captured photo, owned and revoked by the root.
start () => Promise<WebcamMediaStreamLike | undefined> Acquire the camera. Resolves with the stream, or undefined on failure or external mode.
stop () => void Stop and release an owned stream. External streams are never stopped.
switchCamera () => Promise<void> Toggle between user and environment, restarting an active owned stream.
selectDevice (deviceId: string | undefined) => Promise<void> Select one camera, restarting an active owned stream.
capture () => Promise<WebcamCapturePhotoResult | null> Capture one frame from the preview. Resolves with null when no frame is available.
clearPhoto () => void Forget the last photo and revoke its object URL.

WebcamCaptureShutter

Source: src/families/media/webcam-capture/webcam-capture-shutter.vue

Props

Prop Type Default Description
countdown number 0 Seconds to count down (announced through WebcamCaptureStatusMessage) before capturing.
ariaLabel string undefined Accessible name for icon-only buttons. The default slot renders messages.shutter.

Events

Event Payload Description
click [nativeEvent: MouseEvent] Fired before capturing. Call preventDefault() to cancel.

Slots

Slot Slot props Description
default WebcamCaptureSlotState Button content. Receives the camera state, including the running countdown.

Exposed

Member Type Description
disabled —
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.

WebcamCaptureStartButton

Source: src/families/media/webcam-capture/webcam-capture-start-button.vue

Props

Prop Type Default Description
ariaLabel string undefined Accessible name for icon-only buttons. The default slot renders messages.start.

Events

Event Payload Description
click [nativeEvent: MouseEvent] Fired before the action. Call preventDefault() to cancel it.

Slots

Slot Slot props Description
default WebcamCaptureSlotState Button content. Receives the camera state.

Exposed

Member Type Description
disabled —
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.

WebcamCaptureStatusMessage

Source: src/families/media/webcam-capture/webcam-capture-status-message.vue

Slots

Slot Slot props Description
default WebcamCaptureSlotState & { readonly message: string } Announcement content. Defaults to the localized status, countdown, or capture text.

Exposed

Member Type Description
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.
message —

WebcamCaptureStopButton

Source: src/families/media/webcam-capture/webcam-capture-stop-button.vue

Props

Prop Type Default Description
ariaLabel string undefined Accessible name for icon-only buttons. The default slot renders messages.stop.

Events

Event Payload Description
click [nativeEvent: MouseEvent] Fired before the action. Call preventDefault() to cancel it.

Slots

Slot Slot props Description
default WebcamCaptureSlotState Button content. Receives the camera state.

Exposed

Member Type Description
disabled —
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.

WebcamCaptureSwitchCamera

Source: src/families/media/webcam-capture/webcam-capture-switch-camera.vue

Props

Prop Type Default Description
ariaLabel string undefined Accessible name for icon-only buttons. The default slot renders messages.switchCamera.

Events

Event Payload Description
click [nativeEvent: MouseEvent] Fired before the action. Call preventDefault() to cancel it.

Slots

Slot Slot props Description
default WebcamCaptureSlotState Button content. Receives the camera state.

Exposed

Member Type Description
disabled —
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.

WebcamCaptureVideo

Source: src/families/media/webcam-capture/webcam-capture-video.vue

Props

Prop Type Default Description
ariaLabel string undefined Accessible preview name. Defaults to messages.preview.

Slots

Slot Slot props Description
default WebcamCaptureSlotState Fallback content for browsers without video support.

Exposed

Member Type Description
element HTMLVideoElement | null Rendered native video element.

Behavior

Normative state x input -> outcome table for webcam-capture-root.vue, webcam-capture-video.vue, webcam-capture-start-button.vue, webcam-capture-stop-button.vue, webcam-capture-switch-camera.vue, webcam-capture-device-select.vue, webcam-capture-shutter.vue, webcam-capture-photo.vue, and webcam-capture-status-message.vue (@vizejs/ui/webcam-capture). Every row is proven by the named test.

The root either owns a camera stream (acquired through getUserMedia only after start() or autoStart on mount) or displays a consumer stream passed through stream, such as useUserMedia().stream from @vizejs/composable. Status values and error codes are identical to the composable's MediaStreamStatus and MediaStreamErrorCode. Consumer streams are never acquired, restarted, or stopped.

ID State Input Outcome Evidence
W1 idle mount renders idle parts with localized defaults, a mirrored muted inline preview, a polite live region, and never requests permission renders an idle camera without requesting permission
W2 idle Start requests { audio, video: { ...constraints, facingMode } }, announces requesting/active, binds srcObject, and lists video inputs start acquires the camera, binds the preview, and lists devices
W3 requesting rejection / missing API maps errors with the composable table (permission-denied, unsupported, …), emits error, announces the localized reason, allows retry acquisition failures are normalized like useUserMedia
W4 active (owned) Stop / all tracks end / late stream stops owned tracks, unbinds the preview, returns to idle; a stream resolved after stop is released immediately stop and ended tracks release owned streams, and late streams are discarded
W5 active (owned) Switch camera / device select toggles facing mode (mirroring follows user) or selects an exact device, emits update:*, and restarts the owned stream switching cameras and selecting devices restart an owned stream
W6 external stream render / start / stop / unmount shows the consumer stream as active, disables owned controls, never calls getUserMedia, never stops consumer tracks external streams are displayed but never acquired or stopped
W7 active Shutter draws a mirrored, center-cropped, downscaled frame, encodes with captureType/captureQuality, renders the photo, revokes old URLs the shutter captures a mirrored, cropped frame and owns the photo URL
W8 idle / no frame capture captures nothing while idle; draw and encode failures emit captureError with typed diagnostics capture failures emit captureError and a missing frame captures nothing
W9 active Shutter with countdown announces each remaining second, disables the shutter, captures after the countdown; stop cancels it countdowns announce each second, block the shutter, and cancel on stop
W10 any messages every default label and announcement is replaced by the typed messages prop messages localize every default label and announcement
W11 autoStart mount / devicechange requests on mount (client only) and refreshes the device list on devicechange; mirrored overrides the facing-mode default autoStart requests on mount and devicechange refreshes the list
W12 active with photo unmount stops owned tracks and revokes the photo object URL unmounting stops owned tracks and revokes the photo URL
W13 controls click with preventDefault() leaves the camera untouched buttons honor preventDefault from click listeners
W14 missing provider setup parts fail closed with the shared context diagnostic compound parts require a matching root provider
W15 helpers pure functions error normalization, constraints, device lists, message merging, and capture geometry are deterministic webcam-capture-media.test.ts
W16 SSR isolated requests idle markup is byte-identical and autoStart never touches devices on the server renders byte-identical idle markup without requesting the camera on the server
W17 SSR / hydration hydrate server markup hydrates without warnings or node replacement hydrates idle camera markup without warnings or node replacement
W18 types compile statuses and codes match the composable, native MediaStream/MediaDevices are structurally accepted, options are closed webcam-capture.types.test-d.ts

Mirroring is published as data-mirrored and --vize-ui-webcam-capture-scale-x (-1 or 1); apply it with transform: scaleX(var(--vize-ui-webcam-capture-scale-x)). Browsers only accept native MediaStreams as srcObject; the root binds the raw (unproxied) stream and skips structural stand-ins.