Is there a way to have an arbitrary HTML element rendered in a canvas (and then access its buffer...).
-
7You should check out [html2canvas](http://html2canvas.hertzen.com/). – Linus Thiel Sep 29 '12 at 12:40
6 Answers
You won't get real HTML rendering to <canvas>
per se currently, because canvas context does not have functions to render HTML elements.
There are some emulations:
html2canvas project http://html2canvas.hertzen.com/index.html (basically a HTML renderer attempt built on Javascript + canvas)
HTML to SVG to <canvas>
might be possible depending on your use case:
https://github.com/miohtama/Krusovice/blob/master/src/tools/html2svg2canvas.js
Also if you are using Firefox you can hack some extended permissions and then render a DOM window to <canvas>

- 82,057
- 50
- 264
- 435
-
Currently (as of 2016.05.25), html2canvas is buggy (e.g. generated a 0x0 canvas for me; generated no visible HTML text on the canvas when I fixed the canvas to be of some size). I tried to fix it, but this was not easy, so I gave up. – Eugene Gr. Philippov May 24 '16 at 22:39
-
1@EvgeniyPhilippov: This sounds like a bug and should be reported to the relevant issue tracker. The project is pretty popular, it works for multiple users. If there is something why it doesn't work for you then it should be properly reported. – Mikko Ohtamaa May 25 '16 at 13:32
-
2html2canvas seems complete overkill for something that can be done in about 20 lines of code... – CpnCrunch May 01 '17 at 18:03
Take a look at MDN's article on Drawing DOM objects into a canvas:
Given HTML loaded into a JS string
value, it can render it to a <canvas>
or <img>
element. This is done with the help of SVG's <foreignObject>
element.
Here are working examples in Javascript of rendering a HTML string
to either a <canvas>
element, or to an <img />
element:
Render HTML to <canvas>
Element:
const renderThisHtml = `<em>I</em> like <span style="color:white; text-shadow:0 0 2px blue;">cheese</span> `;
const butMakeItLargerForStackOverflowDemoPurposes = `<span style="font-size: 30px">${renderThisHtml}</span>`;
renderHtmlToCanvas( document.getElementById( 'myCanvas' ), butMakeItLargerForStackOverflowDemoPurposes );
function renderHtmlToCanvas( canvas, html ) {
const ctx = canvas.getContext( '2d' );
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" width="${canvas.width}" height="${canvas.height}">
<foreignObject width="100%" height="100%">
<div xmlns="http://www.w3.org/1999/xhtml">${html}</div>
</foreignObject>
</svg>`;
const svgBlob = new Blob( [svg], { type: 'image/svg+xml;charset=utf-8' } );
const svgObjectUrl = URL.createObjectURL( svgBlob );
const tempImg = new Image();
tempImg.addEventListener( 'load', function() {
ctx.drawImage( tempImg, 0, 0 );
URL.revokeObjectURL( svgObjectUrl );
} );
tempImg.src = svgObjectUrl;
}
<canvas id="myCanvas" style="border:2px solid black;" width="200" height="200"></canvas>
Render HTML to <img />
Image Element:
You can also render the HTML to an <img>
element in your document with a few tweaks:
const renderThisHtml = `<em>I</em> like <span style="color:white; text-shadow:0 0 2px blue;">cheese</span> `;
const butMakeItLargerForStackOverflowDemoPurposes = `<span style="font-size: 30px">${renderThisHtml}</span>`;
renderHtmlToImg( document.getElementById( 'myImg' ), butMakeItLargerForStackOverflowDemoPurposes );
function renderHtmlToImg( img, html ) {
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" width="${img.width}" height="${img.height}">
<foreignObject width="100%" height="100%">
<div xmlns="http://www.w3.org/1999/xhtml">${html}</div>
</foreignObject>
</svg>`;
const svgBlob = new Blob( [svg], { type: 'image/svg+xml;charset=utf-8' } );
const svgObjectUrl = URL.createObjectURL( svgBlob );
const oldSrc = img.src;
if( oldSrc && oldSrc.startsWith( 'blob:' ) ) { // See https://stackoverflow.com/a/75848053/159145
URL.revokeObjectURL( oldSrc );
}
img.src = svgObjectUrl;
}
<img id="myImg" style="border:2px solid black;" width="200" height="200" />

- 141,631
- 28
- 261
- 374

- 1,458
- 15
- 34
-
1While that works with your example, it won't work with arbitrary html, as you need to convert the html to xml. – CpnCrunch May 01 '17 at 18:13
-
What if we want to do a div that has images as children? When I do this the images don't show up. What am I missing? – Jess the Mess Jan 04 '18 at 21:41
-
-
2For anyone else coming here looking for enlightenment: `foreignObject` is not supported in IE. – icfantv Mar 07 '19 at 23:49
-
-
@Nick, xml is not html. There are many examples of html code which will fail to work if you don't serialize to xml. (I don't have any in mind right now because I wrote this code years ago, but the reason I wrote it is because the code above simply didn't work). – CpnCrunch Jan 15 '20 at 18:47
-
@CpnCrunch Yeah I know that they are different. But so far I've not encountered any issues with this technique, works fine – Nick Jan 17 '20 at 12:06
-
@Nick, try your answer in Safari...when you click "Run code snippet" above, it is completely blank. – CpnCrunch Jan 19 '20 at 23:03
-
Blob should not be used `const svgBlob = new Blob( [svg], { type: 'image/svg+xml;charset=utf-8' } );` This will cause the canvas to be tainted and the image will not be exported. It should be assigned directly to the `img.src` property `img.src = \`data:image/svg+xml;charset=utf-8,${svg}\`` – fenghen Apr 07 '23 at 05:02
Here is code to render arbitrary HTML into a canvas:
function render_html_to_canvas(html, ctx, x, y, width, height) {
var xml = html_to_xml(html);
xml = xml.replace(/\#/g, '%23');
var data = "data:image/svg+xml;charset=utf-8,"+'<svg xmlns="http://www.w3.org/2000/svg" width="'+width+'" height="'+height+'">' +
'<foreignObject width="100%" height="100%">' +
xml+
'</foreignObject>' +
'</svg>';
var img = new Image();
img.onload = function () {
ctx.drawImage(img, x, y);
}
img.src = data;
}
function html_to_xml(html) {
var doc = document.implementation.createHTMLDocument('');
doc.write(html);
// You must manually set the xmlns if you intend to immediately serialize
// the HTML document to a string as opposed to appending it to a
// <foreignObject> in the DOM
doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
// Get well-formed markup
html = (new XMLSerializer).serializeToString(doc.body);
return html;
}
example:
const ctx = document.querySelector('canvas').getContext('2d');
const html = `
<p>this
<p>is <span style="color:red; font-weight: bold;">not</span>
<p><i>xml</i>!
<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABWElEQVQ4jZ2Tu07DQBBFz9jjvEAQqAlQ0CHxERQ0/AItBV9Ew8dQUNBQIho6qCFE4Nhex4u85OHdWAKxzfWsx0d3HpazdGITA4kROjl0ckFrnYJmQlJrKsQZxFOIMyEqIMpADGhSZpikB1hAGsovdxABGuepC/4L0U7xRTG/riG3J8fuvdifPKnmasXp5c2TB1HNPl24gNTnpeqsgmj1eFgayoHvRDWbLBOKJbn9WLGYflCCpmM/2a4Au6/PTjdH+z9lCJQ9vyeq0w/ve2kA3vaOnI6k4Pz+0Y24yP3Gapy+Bw6qdfsCRZfWSWgclCCVXTZu5LZFXKJJ2sepW2KYNCENB3U5pw93zLoDjNK6E7rTFcgbkGYJtiLckxCiw4W1OURsxUE5BokQiQj3JIToVtKwlhsurq+YDYbMBjuU/W3KtT3xIbrpAD7E60lwQohuaMtP8ldI0uMbGfC1r1zyWPUAAAAASUVORK5CYII=">`;
render_html_to_canvas(html, ctx, 0, 0, 300, 150);
function render_html_to_canvas(html, ctx, x, y, width, height) {
var data = "data:image/svg+xml;charset=utf-8," + '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '">' +
'<foreignObject width="100%" height="100%">' +
html_to_xml(html) +
'</foreignObject>' +
'</svg>';
var img = new Image();
img.onload = function() {
ctx.drawImage(img, x, y);
}
img.src = data;
}
function html_to_xml(html) {
var doc = document.implementation.createHTMLDocument('');
doc.write(html);
// You must manually set the xmlns if you intend to immediately serialize
// the HTML document to a string as opposed to appending it to a
// <foreignObject> in the DOM
doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
// Get well-formed markup
html = (new XMLSerializer).serializeToString(doc.body);
return html;
}
<canvas></canvas>

- 4,831
- 1
- 33
- 31
-
2
-
This is definitely not a reliable way, as it doesn't even allow you to render images, I do not even consider video elements or other dynamic data. – Footniko Feb 25 '19 at 20:21
-
-
@cancerbero It seems to work reliably on Safari, Firefox and Chrome. We are using it in production code. I just updated the code to fix an issue with hash characters. – CpnCrunch Sep 14 '19 at 13:22
-
There seems to be an issue with the `html_to_xml` function. All it returns for me is `[object HTMLDocument]`. – MLK.DEV Jun 16 '20 at 18:48
-
It should return a string. See https://developer.mozilla.org/en-US/docs/Web/API/XMLSerializer/serializeToString – CpnCrunch Jun 17 '20 at 21:52
-
-
The CSS element()
function may eventually help some people here, even though it's not a direct answer to the question. It allows you to use an element (and all children, including videos, cross-domain iframes, etc.) as a background image (and anywhere else that you'd normally use url(...)
in your CSS code). Here's a blog post that shows what you can do with it.
It has been implemented in Firefox since 2011, and is being considered in Chromium/Chrome (don't forget to give the issue a star if you care about this functionality).
-
Unfortunately, as of July 2022, `element()` still has very poor browser support everywhere but Firefox: https://caniuse.com/css-element-function – flyingace Jun 26 '22 at 16:53
RasterizeHTML is a very good project, but if you need to access the canvas it wont work on chrome. due to the use of <foreignObject>
.
If you need to access the canvas then you can use html2canvas
I am trying to find another project as html2canvas is very slow in performance

- 141,631
- 28
- 261
- 374

- 31
- 4
-
Hi, your answers seems somewhat garbled ("due to the use of" what?). Maybe some bit went missing? – jochen Dec 02 '14 at 11:44
According to the HTML specification you can't access the elements of the Canvas. You can get its context, and draw in it manipulate it, but that is all.
BUT, you can put both the Canvas and the html element in the same div with a aposition: relative
and then set the canvas and the other element to position: absolute
.
This ways they will be on the top of each other. Then you can use the left
and right
CSS properties to position the html element.
If the element doesn't shows up, maybe the canvas is before it, so use the z-index
CSS property to bring it before the canvas.

- 196
- 1
- 12
-
1Sorry, probably I haven't been very precise... I was meaning: is it possible to render an html element on the canvas frambuffer. – gotch4 Sep 29 '12 at 22:22