Having trouble getting the HTML5 Canvas element to display on iOS Chrome or Safari?

I have developed a website that utilizes a user's webcam frames through an HTML <video> element. These frames are then copied to a <canvas> element, which is displayed using opencv.js' cv.imshow(). Additionally, the frame is duplicated onto an off-screen canvas for server-side processing, producing the intended outcome.

The website performs as expected on Chrome (including browsers like Brave) on desktop (Linux) and Android Chrome. However, there seems to be an issue with iOS Chrome and Safari where the canvas appears as plain white rather than displaying the video frames. This discrepancy verifies that the camera frames from the video element are being captured accurately.

In order to troubleshoot this problem, I implemented the following functions:

To check if the canvas is blank:

// Returns true if all color channels in each pixel are 0 (or "blank")
function isCanvasBlank(canvas) {
    return !canvas.getContext('2d')
      .getImageData(0, 0, canvas.width, canvas.height).data
      .some(channel => channel !== 0);
}

To verify the visibility of the canvas element:

const observer = new IntersectionObserver((entries) => {
    if(entries[0].isIntersecting){
        // The element is visible
        console.log('canvas_visible', true);
    } else {
        // The element is not visible
        console.log('canvas_visible', false);
    }
});
observer.observe(canvas);

Upon inspecting chrome://inspector on iOS, the output displays is_canvas_blank false and canvas_visible true after each frame.

An interesting observation is that running the opencv.js tutorial on iOS Chrome successfully displays the frames on the canvas (refer to the opencv.js videocapture tutorial). My own implementation closely follows the steps outlined in this tutorial (see minimal implementation below).

HTML

<div id="localVideo">
    <video id="video"></video>
    <canvas id="canvas" height="480px" width="640px"></canvas>
    <canvas id="hiddenCanvas" height="480px" width="640px"></canvas>
</div>

JS

let video = document.getElementById('video');
let cap = new cv.VideoCapture(video);
let src = new cv.Mat(video.videoHeight, video.videoWidth, cv.CV_8UC4);
let dst = new cv.Mat(video.videoHeight, video.videoWidth, cv.CV_8UC1);

const FPS = 15;
function processVideo() {
    try {
        if (!streaming) {
            // Cleanup and stop.
            delete cap;
            src.delete();
            dst.delete();
        }
        cap.read(src);
        cv.cvtColor(src, dst, cv.COLOR_RGBA2RGB);

        cv.imshow('canvas', dst);    
        cv.imshow('hiddenCanvas', src);

        let delay = 1000 / FPS;
        setTimeout(processVideo, delay);
    } catch (err) {
        console.log(err);
    }
}
// Begin processing the video.
setTimeout(processVideo(), 0);

The HTML <video> element also correctly displays the video on iOS Chrome.

If any frontend developer could shed light on why this issue pertains only to iOS Chrome/Safari, why the tutorial functions whereas my code does not, or if I am overlooking anything, it would be greatly appreciated. Thank you.

Screenshots from iOS Chrome are attached for reference:

Initial page load - Canvas with the image source set to PNG displaying correctly:
https://i.sstatic.net/snJNe.jpg

Displaying the video element showing the user's cam - operating as expected:
https://i.sstatic.net/6nwcv.jpg

Video element hidden and canvas element unhidden - no rendering on the canvas
https://i.sstatic.net/ot3I3.jpg

Expected result (as observed on Android Chrome):
https://i.sstatic.net/iJ0Hc.jpg

Answer №1

The problem arose due to a CSS transform (rotateY(180)) being used on the canvas element.

Interestingly, Android's Chrome browser can handle this without issue, but iOS Chrome and Safari struggle to render the canvas properly.

To fix this issue, OpenCV can be utilized to flip the frame using the command cv.flip(src, src, 1) if you prefer to mirror the image instead.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to differentiate between several elements inserted via JavaScript in my HTML code?

I'm attempting to replicate the image below using HTML and JavaScript: https://i.sstatic.net/kvmqk.jpg So far, this is what I have: https://i.sstatic.net/8iHfB.png Here's my JavaScript code: function createLink(text, parentElement) { var ...

Launching the ngx Modal following an Angular HTTP request

Trying to trigger the opening of a modal window from an Angular application after making an HTTP call can be tricky. Below is the content of app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/pla ...

What are the most effective techniques for setting up headers in a table for optimal organization?

I have a dataset in a table format and I am looking to adjust the spacing to avoid overcrowding. At the same time, I want the title row of the table to not follow the same spacing rules as the rest of the cells. Is there a way to achieve this without crea ...

Error encountered in Internet Explorer 11 - Access is forbidden - XMLHttpRequest

I am encountering a unique issue with IE11 and ajax. Most of the time, everything works as expected when I use the code below for my requests. However, I have noticed that when I try to use it in combination with a copy and paste function, an 'Access ...

When linking to a section, the Bootstrap navbar obscures the top part of

I have been working on my inaugural website for educational purposes and encountered the following issue: Every time I try to link to a specific section on the same page, it opens as intended but the top portion is obscured by the navbar. <body data-s ...

Passing PHP Variables Between Pages

I'm currently working on building a game using html5(phaser js) and I need to create a leaderboard. Here's the code snippet I have: restart_game: function() { // Start the 'main' state, which restarts the game //this.game.time.events ...

Organize the search bar and button over a background image using Bootstrap and CSS styling

I am facing a challenge in positioning the search bar on a background image, as well as placing a button at the bottom and center of the image. After that, I plan to include containers. My Goal https://i.sstatic.net/ZfYQj.png However, I am struggling an ...

Error message encountered: "myData undefined while executing server in Node.js"

const express = require('express'); const app = express(); app.post('/', (req, res) => { let newData = new contact(req.body); newData.save() .then(() => { res.send("Data has been successfully saved to ...

Having trouble with Bootstrap 5 Carousel not sliding to the next image? Learn how to fix this issue by upgrading from Bootstrap 4 to Bootstrap 5

Having downloaded both the compiled css and js and the source files of the bootstrap 5 library, I've experimented with both. While the css seems to load fine, I'm struggling to get this example to function. It's from a bootstrap 4 related po ...

Locating the CSS pixel value without being affected by the browser's zoom settings

Check out this JS fiddle I created to illustrate my issue. As you zoom in and out of the browser page, the value of max-width that appears in the console keeps changing. However, in the CSS code it is always set to 500px. How can I retrieve the original CS ...

What is the best way to incorporate this CodePen snippet into a Vue project?

Can anyone help me figure out how to incorporate this awesome animation from CodePen (link here: https://codepen.io/iprodev/pen/azpWBr) into a Vue project? I've tried implementing it like so: <template> <div> <canvas heigh ...

Utilizing CSS to Implement a Background Image

Here is where I am looking to upload the image. Ideally, I would like to position my image to the left side of the text related to Food and Travel. You can view the image here. .block-title h3 { color: #151515; font-family: 'Montserrat', s ...

The HTML table printout is missing cells in the first column

Encountered a strange issue with Internet Explorer and Chrome involving a basic HTML table. The table has no layout CSS, 2 columns, no styles, and its width is set to 100%. When attempting to print the table in these browsers, the first cell on the second ...

showcasing information retrieved from the database on a PHP page, with the option to delete it by selecting a checkbox and utilizing AJAX

Hello everyone, I am currently working on a PHP project where I need to display data from a database on a page and then remove them from the page (not the database) when a checkbox is selected using Ajax. Do you have any recommended sources or links that c ...

The lower div is overlapping the upper div in a smaller viewport

I have integrated some dashboard widgets in iframes and I want to ensure they are responsive. My goal is for each widget to occupy full width when the screen size is reduced, but appear side by side when the browser window is large (md+): iframe { b ...

Next.js does not support Video.js functionality when using server side rendering

I'm struggling to set up video.js in my next.js project and encountering issues. When the player is loading, it initially appears black and then disappears abruptly. A warning message in the console reads: "video.es.js?31bb:228 VIDEOJS: WARN: T ...

What is the best way to get the *ngfor to function properly within a modal window?

I am encountering an issue with the *ngfor directive as the modal is not functioning correctly. <table *ngFor="let wt of wtl"> <tr> <td *> <mat-form-field style=&quo ...

Iterating through each ID in a table/cell using .NET MVC with the @

Whenever the table is loaded, I need to run a JavaScript function on each row for cell 4. This function will format the JSON string that is inserted into it. The current code I have only updates the first row/cell... I believe the issue may be related to ...

When attempting to upload multiple images to my backend, I am consistently receiving an empty array as a result

I have developed an API on the backend that is designed to store images in the database. I have implemented a function for uploading these images, but unfortunately, I am encountering an issue where my database receives an empty array. Interestingly, when ...

Is it possible for Vue data to be handled asynchronosly

Have you ever wondered? Is it possible for Vue's data function to be asynchronous? Imagine needing to fetch data from an API using a library like axios, which only offers async methods. How can this data be loaded into Vue's data function? Con ...