HTML in Canvas
HTML-in-Canvas lets dom and vue objects render an HTML interface in a canvas. Use the canvas as a video source or apply local effects.
Experimental: This feature uses Chromium's experimental HTML-in-Canvas API. Enable
chrome://flags/#canvas-draw-elementin Chromium.
How It Works
dom and vue normally render interactive HTML directly on the Patchies canvas. HTML-in-Canvas puts the interface in a layoutsubtree canvas. Patchies can then draw or capture the DOM pixels.
There are two separate use cases:
htmlCanvas.videoOutput()captures the HTML as a transferableElementImage. It sends the image to the render worker as a video source.htmlCanvas.canvasLayer()andhtmlCanvas.glslLayer()process the interface on the main thread. They do not add a video outlet.
The local layer APIs keep the interface clickable. Use one htmlCanvas mode at a time: videoOutput, canvasLayer, or glslLayer. Patchies logs an error when an object enables multiple modes in one run.
Video Output
Call htmlCanvas.videoOutput() to use the object as a video source. Patchies captures the root element as a transferable ElementImage. It draws the image in the render worker:
htmlCanvas.videoOutput();
setSize(640, 360);
root.innerHTML = '<div class="grid h-full place-items-center text-4xl">hello</div>';
By default, htmlCanvas.videoOutput() uses the current Patchies render output size. Use htmlCanvas.videoOutput({ size: "free" }) to let DOM or Vue content choose the source size. Patchies then fits it into the render output.
videoOutput cannot be combined with canvasLayer or glslLayer.
Disable the video output with:
htmlCanvas.videoOutput(false);
Canvas Layer
Call htmlCanvas.canvasLayer(callback) to process the interface with a local 2D canvas. This function does not register a video output:
htmlCanvas.canvasLayer((ctx, frame) => {
ctx.fillStyle = 'rgba(255, 0, 0, 0.15)';
ctx.fillRect(0, 0, frame.displayWidth, frame.displayHeight);
});
The callback gets { width, height, displayWidth, displayHeight, pixelRatio, time, delta }.
widthandheightare backing canvas pixels.displayWidthanddisplayHeightare CSS pixels.pixelRatiois the device pixel ratio used for the backing canvas.
Disable the layer with:
htmlCanvas.canvasLayer(false);
canvasLayer cannot be combined with videoOutput or glslLayer.
GLSL Layer
Call htmlCanvas.glslLayer(fragmentShader) to process the interface with a local WebGL2 GLSL ES 3 fragment shader. The HTML pixels are available as sampler2D source:
htmlCanvas.glslLayer(`
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec4 color = texture(source, uv);
float wave = sin((uv.y * 28.0) + iTime * 5.0) * 0.012;
vec2 warpedUv = uv + vec2(wave, 0.0);
vec4 warped = texture(source, warpedUv);
float scanline = 0.92 + 0.08 * sin(fragCoord.y * 1.7);
vec3 tint = warped.rgb * vec3(1.12, 0.96, 1.05) * scanline;
float vignette = smoothstep(0.95, 0.25, distance(uv, vec2(0.5)));
fragColor = vec4(tint * vignette, warped.a);
}
`);
The built-in uniforms are source, iResolution, iTime, iTimeDelta, and iFrame.
Use texture(source, uv) to sample the interface. htmlCanvas.glslLayer() uses the same ShaderToy-style GLSL wrapper as the glsl object. It supports GLSL ES 3 syntax and #include preprocessing.
Disable the layer with:
htmlCanvas.glslLayer(false);
glslLayer cannot be combined with videoOutput or canvasLayer.
Vue Example
Use the same htmlCanvas APIs before you mount a Vue app:
htmlCanvas.glslLayer(`
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec4 color = texture(source, uv);
fragColor = vec4(color.rgb * vec3(1.0, 1.15, 1.05), color.a);
}
`);
createApp({
template: '<button class="p-4">still clickable</button>'
}).mount(root);
Browser Support
If Chromium does not provide the required API, Patchies logs a warning. The object remains in normal DOM or Vue mode.
These APIs are experimental. They can change when Chromium changes its HTML-in-Canvas implementation.
See Also
- dom — Create a vanilla JavaScript interface.
- vue — Create a Vue.js interface.
- GLSL Imports — Share GLSL code with
#include. - Video Chaining — Connect visual objects as video sources.