Camera API

The Camera API provides fine-grained control over camera behavior during scanning sessions.

Overview

The camera property on the AnylineJS instance exposes methods for controlling the camera stream, including mirroring and flipping, switching cameras, handling device sleep issues, manual focus control, and flash control.

const anyline = init({ /* ... */ });

// Access camera API
anyline.camera.mirrorStream(true);
anyline.camera.flipStream(true);
anyline.camera.flipFramesLeftRight(true);
anyline.camera.flipFramesTopBottom(true);
anyline.camera.setCamera(deviceId);
await anyline.camera.refocus();
await anyline.camera.activateFlash(true);
anyline.camera.reappend();
The SDK distinguishes the preview (what the operator sees) from the OCR frame (what the recognizer reads). mirrorStream() and flipStream() transform the preview only; flipFramesLeftRight() and flipFramesTopBottom() transform the OCR frame only. For a camera mounted 180° (upside down), enable both OCR-frame flips so text is recognized, and optionally both preview flips so the operator sees an upright image.
The cutout always defines the scan region: whatever is visible inside the cutout on screen is exactly what the recognizer processes, regardless of any combination of preview and OCR-frame flips. Scan feedback likewise always appears inside the cutout, aligned with the previewed content.

Methods

mirrorStream(state: boolean): void

Mirrors the camera stream horizontally.

Parameters:

  • state - true to enable mirroring, false to disable

Use Case:

Front-facing cameras typically display a mirrored view to match user expectations (selfie mode). This method allows you to control mirroring behavior manually.

Platform Support: All platforms

Example:

// Enable mirroring for front-facing camera
anyline.camera.mirrorStream(true);

// Disable mirroring for back-facing camera
anyline.camera.mirrorStream(false);
The SDK automatically handles mirroring based on camera facing mode. Use this method only when you need to override the default behavior.

flipStream(state: boolean): void

Flips the camera preview upside down (vertically). This is the vertical partner to mirrorStream() and, like it, affects only the on-screen preview, not the image sent to the recognizer. The scan region follows the cutout: what is shown inside the cutout after the flip is what gets scanned.

Parameters:

  • state - true to flip the preview upside down, false to restore

Use Case:

Show an upright preview to the operator when the camera is physically mounted upside down. Combine with mirrorStream(true) for a camera mounted 180°.

Platform Support: All platforms

Example:

// Flip the preview upside down
anyline.camera.flipStream(true);

isMirrored(): boolean

Returns the current preview mirror state.

Returns: true if the preview is horizontally mirrored, otherwise false.

Platform Support: All platforms

Example:

const mirrored = anyline.camera.isMirrored();

flipFramesLeftRight(state: boolean): void

Flips the image sent to the recognizer horizontally. This does not affect the on-screen preview.

Parameters:

  • state - true to flip the OCR frame horizontally, false to disable

Use Case:

Correct a horizontally mirrored camera so text is read the right way round. Can also be set at initialization with the enableFlipFramesLeftRight option.

Platform Support: All platforms

Example:

anyline.camera.flipFramesLeftRight(true);

flipFramesTopBottom(state: boolean): void

Flips the image sent to the recognizer vertically. This does not affect the on-screen preview.

Parameters:

  • state - true to flip the OCR frame vertically, false to disable

Use Case:

Correct a vertically flipped camera so text is read upright. Combine with flipFramesLeftRight(true) for a camera mounted 180°. Can also be set at initialization with the enableFlipFramesTopBottom option.

Platform Support: All platforms

Example:

anyline.camera.flipFramesTopBottom(true);

setCamera(deviceId: string): void

Switches to a specific camera device.

Parameters:

  • deviceId - The device ID from the MediaDevices API

Use Case:

Allows users to manually select a camera when multiple cameras are available (e.g., front and back cameras on mobile devices, or multiple webcams on desktop).

Platform Support: All platforms

Example:

// Get available cameras
const devices = await navigator.mediaDevices.enumerateDevices();
const cameras = devices.filter(d => d.kind === 'videoinput');

console.log('Available cameras:', cameras);

// Switch to back camera
const backCamera = cameras.find(c =>
  c.label.toLowerCase().includes('back') ||
  c.label.toLowerCase().includes('rear')
);

if (backCamera) {
  anyline.camera.setCamera(backCamera.deviceId);
}

Complete Camera Selector Example:

async function createCameraSelector(anyline: AnylineJS) {
  const devices = await navigator.mediaDevices.enumerateDevices();
  const cameras = devices.filter(d => d.kind === 'videoinput');

  const select = document.createElement('select');

  cameras.forEach(camera => {
    const option = document.createElement('option');
    option.value = camera.deviceId;
    option.text = camera.label || `Camera ${cameras.indexOf(camera) + 1}`;
    select.appendChild(option);
  });

  select.addEventListener('change', (e) => {
    const deviceId = (e.target as HTMLSelectElement).value;
    anyline.camera.setCamera(deviceId);
  });

  return select;
}
This method executes asynchronously and returns void immediately.
Browser device lists are only populated after camera permission is granted. Ensure startScanning() has run before calling enumerateDevices().
Switching cameras requires an active video stream. Call this method after startScanning() has resolved.
Stream switching failures are handled inside the SDK; on error, the previous stream stays active and a message is logged to the console (or debug overlay when debugAnyline is enabled).

reappend(): void

Reappends the camera stream to the video element.

Use Case:

Fixes camera stream issues that can occur when a device wakes from sleep or when the app/tab regains focus after prolonged suspension. This is particularly common on mobile devices.

Platform Support: All platforms

Common Scenarios:

  • Device screen turns off and back on

  • Browser tab is backgrounded for extended periods

  • Mobile device locks and unlocks

  • System puts browser into low-power mode

Example:

// Reappend camera when window regains focus
window.addEventListener('focus', () => {
  anyline.camera.reappend();
});

// Handle visibility change
document.addEventListener('visibilitychange', () => {
  if (!document.hidden) {
    // Page became visible, reappend camera
    anyline.camera.reappend();
  }
});

Best Practice Example:

// Comprehensive camera stream recovery
let wasHidden = false;

document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    wasHidden = true;
  } else if (wasHidden) {
    // Only reappend if page was actually hidden
    console.log('Recovering camera stream...');
    anyline.camera.reappend();
    wasHidden = false;
  }
});

// Handle page show events (e.g., returning from background on mobile browsers)
window.addEventListener('pageshow', () => {
  if (anyline.getState() === 'scanning') {
    console.log('App resumed, recovering camera...');
    anyline.camera.reappend();
  }
});
This method executes asynchronously and returns void immediately.
Calling reappend() while no scan is active has no effect. Trigger it only after startScanning() has completed at least once.

refocus(): Promise<void>

Manually triggers camera refocus.

Returns: Promise<void> - Resolves when refocus completes

Throws: OnlyAndroidChromeError - If the browser doesn’t support this feature

Use Case:

Useful when scanning objects at varying distances or when the camera loses focus. Particularly helpful for barcode or text scanning where focus is critical.

Platform Support: Chrome for Android only

Browser Compatibility:

This method relies on camera focus controls exposed via the MediaStream Image Capture API. While Safari 18.4 added basic Image Capture API support, the focus capabilities needed for manual refocus are not available on iOS Safari, so this method remains supported only on Chrome for Android. Other browsers will throw OnlyAndroidChromeError.

If scanning is not currently running (no active camera stream), the promise resolves without making any changes. Call this method only after startScanning() has completed.

Example:

import { OnlyAndroidChromeError } from '@anyline/anyline-js';

// Refocus button handler
async function handleRefocusClick() {
  try {
    await anyline.camera.refocus();
    console.log('Camera refocused successfully');
  } catch (error) {
    if (error instanceof OnlyAndroidChromeError) {
      console.warn('Manual refocus not supported on this browser');
    } else {
      console.error('Refocus failed:', error);
    }
  }
}

Conditional UI Example:

// Only show refocus button on supported platforms
async function setupRefocusButton() {
  const refocusBtn = document.createElement('button');
  refocusBtn.textContent = 'Refocus';
  refocusBtn.onclick = async () => {
    try {
      await anyline.camera.refocus();
      showToast('Camera refocused');
    } catch (error) {
      if (error instanceof OnlyAndroidChromeError) {
        console.warn('Manual refocus not supported on this browser');
      } else {
        console.error('Refocus failed:', error);
      }
    }
  };

  document.body.appendChild(refocusBtn);
}

activateFlash(state: boolean): Promise<void>

Controls the camera flash (torch mode).

Parameters:

  • state - true to enable flash, false to disable

Returns: Promise<void> - Resolves when flash state changes

Throws: OnlyAndroidChromeError - If the browser doesn’t support torch control via MediaStream constraints

Use Case:

Provides additional lighting for scanning in low-light conditions. Particularly useful for barcode, meter, or document scanning.

Platform Support: Chrome for Android, Safari 18.4+ (iOS)

Browser Compatibility:

This method uses the camera torch constraint via MediaStreamTrack.applyConstraints(). Support depends on both the browser and the device hardware.

Supported browsers:

  • ✅ Chrome for Android

  • ✅ iOS Safari 17.5.1+ (iOS 17.5.1+) — torch constraint is supported; feature-detect with track.getCapabilities().torch

  • ❌ Desktop browsers (usually no torch hardware)

  • ❌ iOS Safari versions where getCapabilities().torch is not present or returns false

Call this method only after scanning has started so an active camera stream is available.

Example:

import { OnlyAndroidChromeError } from '@anyline/anyline-js';

// Toggle flash
async function toggleFlash(enabled: boolean) {
  try {
    await anyline.camera.activateFlash(enabled);
    console.log(`Flash ${enabled ? 'enabled' : 'disabled'}`);
  } catch (error) {
    if (error instanceof OnlyAndroidChromeError) {
      console.warn('Flash control not supported on this browser');
    } else {
      console.error('Flash toggle failed:', error);
    }
  }
}

Complete Flash Toggle Button:

// Flash toggle with state management
let flashEnabled = false;

async function createFlashToggle() {
  const flashBtn = document.createElement('button');
  flashBtn.className = 'flash-toggle';
  flashBtn.textContent = 'Flash Off';

  flashBtn.onclick = async () => {
    flashEnabled = !flashEnabled;

    try {
      await anyline.camera.activateFlash(flashEnabled);
      flashBtn.textContent = flashEnabled ? 'Flash On' : 'Flash Off';
      flashBtn.classList.toggle('active', flashEnabled);
    } catch (error) {
      if (error instanceof OnlyAndroidChromeError) {
        console.warn('Flash control not supported on this browser');
      } else {
        console.error('Failed to toggle flash:', error);
      }
      flashEnabled = !flashEnabled; // Revert state
    }
  };

  return flashBtn;
}
Flash availability depends on the device’s hardware capabilities. Always feature-detect first (for example, track.getCapabilities().torch) and handle failures. On iOS Safari 18.4, a WebKit bug fix addressed getSettings() returning stale torch values after applying constraints—avoid using getSettings().torch as your only source of truth for UI state.

Browser Support Summary

Method Desktop Browsers Mobile Safari (iOS) Chrome Android

mirrorStream()

✅ Yes

✅ Yes

✅ Yes

flipStream()

✅ Yes

✅ Yes

✅ Yes

isMirrored()

✅ Yes

✅ Yes

✅ Yes

flipFramesLeftRight()

✅ Yes

✅ Yes

✅ Yes

flipFramesTopBottom()

✅ Yes

✅ Yes

✅ Yes

setCamera()

✅ Yes

✅ Yes

✅ Yes

reappend()

✅ Yes

✅ Yes

✅ Yes

refocus()

❌ No

❌ No

✅ Yes

activateFlash()

❌ No

✅ Yes (18.4+)

✅ Yes

setFocusDistance()

❌ No

❌ No

✅ Yes

Safari 18.4 added Image Capture API support, but activateFlash() relies on the MediaStream torch constraint. Torch control became practically available on iOS Safari in the iOS 17.5.1 timeframe; always use runtime feature detection.

Troubleshooting

Camera Stream Black After Sleep

Problem: Camera stream shows black screen after device sleep.

Solution: Use reappend() when page becomes visible:

document.addEventListener('visibilitychange', () => {
  if (!document.hidden) {
    anyline.camera.reappend();
  }
});

Flash/Refocus Not Working

Problem: Flash or refocus methods throw errors.

Solution: Refocus only works on Chrome for Android; flash (torch) works on Chrome for Android and on supported iOS Safari versions.

import { OnlyAndroidChromeError } from '@anyline/anyline-js';

try {
  await anyline.camera.refocus();
} catch (error) {
  if (error instanceof OnlyAndroidChromeError) {
    console.log('Feature not supported on this browser');
  }
}

Camera Switch Takes Too Long

Problem: Switching cameras causes noticeable delay.

Solution: Show loading indicator during camera switch:

showLoadingIndicator();
anyline.camera.setCamera(deviceId);
// Camera switches asynchronously, show indicator until stream restarts

See Also