I am generating a canvas from styled HTML code, using serializer.serializeToString to create an SVG string, encoding the string and setting an IMG src to it, and then using drawImage to a canvas context. This works, in the sense that sometimes, especially with very simple HTML, the desired image is drawn on the canvas.  But often, the image doesn't end up on the canvas (the canvas remains blank/transparent). I suspect that this is a timing issue.  I am using
this.img.addEventListener('load', () => { this.render() })
to wait until the SVG is loaded into the image before rendering the image to the canvas, so that should not be the problem.  Do I have to wait for serializeToString and if so, how?
A somewhat simplified version of the code is below.
[If you want to know why I want to do this, the context is that I am developing a VR app using a-frame, and I want to dynamically generate a display panel (a canvas) from HTML code to be placed in VR.]
class HTMLCanvas {
            constructor(html) {
                if (!html) throw 'Container Element is Required'
                // Create the canvas to be drawn to
                this.canvas = document.createElement('canvas')
                this.ctx = this.canvas.getContext('2d')
                // Set some basic styles for the embedded HTML
                this.html = html
                this.html.style.position = 'absolute'
                this.html.style.top = '0'
                this.html.style.left = '0'
                this.html.style.overflow = 'hidden'
                this.html.style.fontFamily = 'sans-serif'
                // Image used to draw SVG to the canvas element
                this.img = new Image()
                this.img.addEventListener('load', () => {
                    this.render()
                })
                this.serializer = new XMLSerializer()
                this.svgToImg()
            }
            // Set the src to be rendered to the Image
            svgToImg() {
                // Make sure the element is visible before processing
                this.html.style.display = 'block'
                this.width = this.html.offsetWidth
                this.height = this.html.offsetHeight
                this.canvas.width = this.width
                this.canvas.height = this.height
                let docString = this.serializer.serializeToString(this.html)
                docString =
                    '<svg width="' +
                    this.width +
                    '" height="' +
                    this.height +
                    `" xmlns="http://www.w3.org/2000/svg"><foreignObject x="0" y="0" width="` +
                    (this.width + 20) +
                    '" height="' +
                    (this.height + 20) +
                    '">' +
                    docString +
                    '</foreignObject></svg>'
                this.img.src = 'data:image/svg+xml;utf8,' + encodeURIComponent(docString)
                // Hide the html after processing
                this.html.style.display = 'none'
            }
            // Renders the image containing the SVG to the Canvas
            render() {
                this.canvas.width = this.width
                this.canvas.height = this.height
                this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
                this.ctx.drawImage(this.img, 0, 0)
            }
        }
Here's the HTML I have been using for testing.  It is all styled inline because <foreignObject> doesn't like CSS <style> (not quite true, but it gets a lot more complicated):
<div style="background: rgba(255, 255, 255, 0.2); border-radius: 16px; box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); border: 1px solid rgba(255, 255, 255, 0.3); padding: 20px; width: 300px; font-family: Arial, Helvetica, sans-serif;">
    <div style="text-align: center; font-weight: bold">Key</div>
    <div style="display: grid; grid-template-columns: repeat(3, 1fr);">
    
        <div style="justify-self: center; margin: 10px;">
            <div style="width: 40px; height: 40px; border-radius: 50%; margin: auto; box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px,  rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px; background-color: #9adbb4">
            </div>
            <div>
            General Factors
            </div>
        </div>
  
        <div style="justify-self: center; margin: 10px;">
            <div style="width: 40px; height: 40px; border-radius: 50%; margin: auto; box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px,  rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px; background-color: rgb(255, 255, 0)">
            </div>
            <div>
            Private Transport
            </div>
        </div>
  
        <div style="justify-self: center; margin: 10px;">
            <div style="width: 40px; height: 40px; border-radius: 50%; margin: auto; box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px,  rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px; background-color: #dba542">
            </div>
            <div>
            Outcomes
            </div>
        </div>
  
        <div style="justify-self: center; margin: 10px;">
            <div style="width: 40px; height: 40px; border-radius: 50%; margin: auto; box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px,  rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px; background-color: rgb(219, 110, 103)">
            </div>
            <div>
            Active travel
            </div>
        </div>
  
        <div style="justify-self: center; margin: 10px;">
            <div style="width: 40px; height: 40px; border-radius: 50%; margin: auto; box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px,  rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px; background-color: #8371b7">
            </div>
            <div>
            COVID-related
            </div>
        </div>
  
        <div style="justify-self: center; margin: 10px;">
            <div style="width: 40px; height: 40px; border-radius: 50%; margin: auto; box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px,  rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px; background-color: rgb(100, 114, 143)">
            </div>
            <div>
            Rail
            </div>
        </div>
  
        <div style="justify-self: center; margin: 10px;">
            <div style="width: 40px; height: 40px; border-radius: 50%; margin: auto; box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px,  rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px; background-color: rgb(255, 0, 0)">
            </div>
            <div>
            Bus
            </div>
        </div>
  
        <div style="justify-self: center; margin: 10px;">
            <div style="width: 40px; height: 40px; border-radius: 50%; margin: auto; box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px,  rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px; background-color: rgb(255, 255, 153)">
            </div>
            <div>
            Laws and regulations
            </div>
        </div>
  
        <div style="justify-self: center; margin: 10px;">
            <div style="width: 40px; height: 40px; border-radius: 50%; margin: auto; box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px,  rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px; background-color: rgb(0, 255, 0)">
            </div>
            <div>
            Neighbourhood
            </div>
        </div>
  
    </div>
    <div style=" display: grid; grid-template-columns: repeat(3, 1fr);">
        <div style=" width: 40px; height: 40px; justify-self: center; margin: 10px; text-align: center;">
            <div style="color: rgb(0, 0, 0)">
                <svg style= "filter: drop-shadow(2px 2px 2px rgb(0 0 0 / 0.4));" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-arrow-right" viewbox="0 0 16 16">
                <path stroke="currentColor" stroke-width="1.2" fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z" />
                </svg>
            </div>
            <div>
            COMPLEX
            </div>
        </div>
        
        <div style=" width: 40px; height: 40px; justify-self: center; margin: 10px; text-align: center;">
            <div style="color: #00cc00">
                <svg style= "filter: drop-shadow(2px 2px 2px rgb(0 0 0 / 0.4));" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-arrow-right" viewbox="0 0 16 16">
                <path stroke="currentColor" stroke-width="1.2" fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z" />
                </svg>
            </div>
            <div>
            +VE
            </div>
        </div>
        
        <div style=" width: 40px; height: 40px; justify-self: center; margin: 10px; text-align: center;">
            <div style="color: #ff0000">
                <svg style= "filter: drop-shadow(2px 2px 2px rgb(0 0 0 / 0.4));" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-arrow-right" viewbox="0 0 16 16">
                <path stroke="currentColor" stroke-width="1.2" fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z" />
                </svg>
            </div>
            <div>
            -VE
            </div>
        </div>
        
        <div style=" width: 40px; height: 40px; justify-self: center; margin: 10px; text-align: center;">
            <div style="color: #008000">
                <svg style= "filter: drop-shadow(2px 2px 2px rgb(0 0 0 / 0.4));" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-arrow-right" viewbox="0 0 16 16">
                <path stroke="currentColor" stroke-width="1.2" fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z" />
                </svg>
            </div>
            <div>
            SUGGESTED (+VE)
            </div>
        </div>
        
    </div>
</div>
