I’m developing a hybrid app (WebView / Turbo Native) that uses getUserMedia to access the back camera for a PPG/heart rate measurement feature (the user places their finger on the camera).
Problem: Even when I specify constraints like:
{
video: {
deviceId: '...',
facingMode: { exact: 'environment' },
advanced: [{ zoom: 1.0 }]
},
audio: false
}
On iPhone 15 (iOS 18), iOS unexpectedly switches between the wide, ultra-wide, and telephoto lenses during the measurement.
This breaks the heart rate detection, and it forces the user to move their finger in the middle of the measurement.
Question: Is there any way, via getUserMedia/WebRTC, to force iOS to use only the wide-angle lens and prevent automatic lens switching?
I know that with AVFoundation (Swift) you can pick .builtInWideAngleCamera, but I’m hoping to avoid building a custom native layer and would prefer to stick with WebView/JavaScript if possible to save time and complexity.
Any suggestions, workarounds, or updates from Apple would be greatly appreciated!
Thanks a lot!
iPhones with multiple rear cameras (like 11peo) automatically switch between lenses based on lighting conditions and focus distance, even when facingMode: { exact: "environment" } is set. Unfortunately, getUserMedia in WebView doesn’t provide full control over which specific rear camera (wide, ultra-wide, or telephoto) is used.
Instead of you to rely on facingMode, you can try listing available cameras and selecting the one that matches the wide-angle lens.
You can achieve that by:
async function getWideAngleCamera() {
const devices = await navigator.mediaDevices.enumerateDevices();
const backCameras = devices.filter(device => device.kind === "videoinput" && device.label.toLowerCase().includes("back"));
//pick the camera that has 'wide' in the label
const wideCamera = backCameras.find(device => device.label.toLowerCase().includes("wide")) || backCameras[0];
if (!wideCamera) {
console.error("No suitable wide-angle camera found.");
return null;
}
return wideCamera.deviceId;
}
async function startCamera() {
const deviceId = await getWideAngleCamera();
if (!deviceId) return;
const stream = await navigator.mediaDevices.getUserMedia({
video: {
deviceId: { exact: deviceId },
advanced: [{ zoom: 1.0 }]
}
});
document.querySelector("video").srcObject = stream;
}
startCamera();
I recommend to use the ultra-wide angle lens for your use case. because the finger is very close from lenses so the camera will switch to Macro mode automatically
It is fairly easy, if you set the zoom to 0.5 it will keep using the ultra-wide angle lens no matter what.
Alternatively you can use exact with device ID
{
video: {
deviceId: { exact: '...'},
facingMode: { exact: 'environment' },
advanced: [{ zoom: 1.0 }]
},
audio: false
}
to require the specific camera, you would use:
getUserMedia({ video: { deviceId: { exact: myExactCameraOrBustDeviceId, }, }, });
See the documentation
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With