javascript camera
First written: Jul-2023
last changed Aug-2024

Collecting a static image from a browser has a number of hurdles and problems involved, but amazingly - the easiest option actually comes from using the basic html input tag:

html
<input type='file' accept='image/*' capture='camera'>

    info it is important to note that this works with mobile devices but not desktop; a mobile device will present the devices camera, showing the image to be taken, and giving you all the other basic camera options (zoom, switch view, etc) - but not necessarily page edge detection.

    Using the same html (<input type='file'>) tag on a desktop environment will result in a prompt for a file from the filesystem.

This will then use the camera resolution to capture images. This is the main difference from trying to achieve the same result using navigator.mediaDevices.getUserMedia in javascript which will use the video camera and its [typically] lower resolution.

The practical downside to this however is the user experience involves taking the photo, and then being presented with the image and [Retry] and [OK] buttons, but where the image is not resizable (so you can't zoom in to check for blur), and perhaps you don't want these buttons - consider how the camera app works on your phone where you simply take the image and then move on (with the ability to go back to the image if you desire).

Javascript implementation to collect images from the video camera

To implement an image capture via javascript then you can use this option too, but beware it will use the device video camera and its resolution.

It should be noted that IOS seems to behave differently from Android, in that the camera selector works with "facingMode" = "user" or "environment" on an iPad, but on an android (with more than 2 cameras) then you can use "deviceId"

js :: startCamera function
    async function startCamera() {
    let constraints;
    let devices, videoDevices,selectedDevice;
    if (isIOS()) {
        if ( selectedCameraId!='' ) {
            constraints = {
                audio: false,
                video: {
                    facingMode: selectedCameraId
                    , frameRate: {ideal: 5, max: 30}
                }
            }
        }
    }
    else {
        devices        = await navigator.mediaDevices.enumerateDevices();
        videoDevices   = devices.filter(device => device.kind === 'videoinput');
        selectedDevice = videoDevices.find(device => device.deviceId === selectedCameraId);
    
    
        if (selectedDevice) {
            constraints = {
                video: {
                    deviceId   : selectedCameraId
                    , frameRate: {ideal: 5, max: 30} // Set your desired frame rate here (in this example, 15 fps); lower rates will reduce the demand on the device - which will heat up if left on too long
                },
                audio: false
            };
        }
    }
    
    
    if ( constraints ) {
        await navigator.mediaDevices.getUserMedia(constraints).then(function success(stream) {
            _mediaStream = stream;
            _videoElement.srcObject = stream;
    
            _videoElement.setAttribute('autoplay', '');
            _videoElement.setAttribute('muted', '');
            _videoElement.setAttribute('playsinline', '');
        });
    }

js :: takeSnapshot function
function takeSnapshot() {
    const ctx = snapshotCanvas.getContext('2d');
    ctx.drawImage(videoElement, 0, 0, snapshotCanvas.width, snapshotCanvas.height);
    snapshotImage.src = snapshotCanvas.toDataURL('image/'+imgtype.value);
}


link Click to view a demo

square