Files
hyperframes/docs/guides/html-in-canvas.mdx
T
Miguel Ángel 869a3763b2 feat(catalog): HyperFrames branding, HTML-in-Canvas guide, remove captions (#647)
* docs: group VFX blocks under HTML-in-Canvas, captions under Captions in sidebar

* docs: add HTML-in-Canvas guide, Chrome flag disclaimer, remove captions

- Add docs/guides/html-in-canvas.mdx — comprehensive guide covering the
  API, feature detection, re-capture patterns, and catalog blocks
- Add Chrome flag Warning banner to every HTML-in-Canvas block page
- Remove captions blocks from registry (will ship separately)
- Add html-in-canvas guide to docs navigation (top of Guides section)

* feat: update liquid glass, portal, shatter with HyperFrames branding

Replace generic placeholder content with HyperFrames-themed text:
- Liquid Glass: 'Ship videos 10x faster' with stats and gradient text
- Portal: 'Write HTML / Render Video' with HyperFrames nav
- Shatter: 'HTML is Video' with render speed/file size metrics

Re-rendered and uploaded preview videos to S3.
2026-05-06 19:02:15 +02:00

129 lines
5.0 KiB
Plaintext

---
title: "HTML-in-Canvas"
description: "Render live HTML as WebGL textures — GPU shaders, 3D geometry, and cinematic effects on any DOM content."
---
# HTML-in-Canvas
The HTML-in-Canvas API (`drawElementImage`) lets you capture live, rendered DOM elements directly into a canvas at GPU speed. This means you can take any HTML — dashboards, forms, landing pages, app UIs — and render them as textures in WebGL scenes with shaders, 3D transformations, and post-processing effects.
<Warning>
**Chrome flag required.** The `drawElementImage` API is experimental and must be enabled manually:
1. Open `chrome://flags/#canvas-draw-element` in Chrome or Brave
2. Set **CanvasDrawElement** to **Enabled**
3. Restart the browser
HyperFrames enables this flag automatically during rendering (`--enable-features=CanvasDrawElement`), so rendered videos work without manual setup. The flag is only needed for live preview in the Studio.
</Warning>
## How it works
1. Place HTML content inside a `<canvas layoutsubtree>` element
2. The browser renders the HTML children as normal DOM
3. Call `ctx.drawElementImage(element, x, y, w, h)` to capture the rendered pixels into the canvas
4. Use the canvas as a Three.js texture, apply shaders, map to 3D geometry
```html
<!-- 1. HTML content lives inside the canvas -->
<canvas id="capture" layoutsubtree width="1920" height="1080">
<div class="my-dashboard">
<h1>Revenue: $4.2M</h1>
<div class="chart">...</div>
</div>
</canvas>
<!-- 2. WebGL canvas for 3D rendering -->
<canvas id="theater" width="1920" height="1080"></canvas>
```
```javascript
// 3. Capture HTML to canvas
var capCanvas = document.getElementById("capture");
var ctx = capCanvas.getContext("2d");
ctx.drawElementImage(capCanvas.querySelector(".my-dashboard"), 0, 0, 1920, 1080);
// 4. Use as Three.js texture
var texture = new THREE.CanvasTexture(capCanvas);
var material = new THREE.MeshBasicMaterial({ map: texture });
```
## What makes this different
Traditional approaches like `html2canvas` re-parse and re-render the DOM in JavaScript — they're slow, lossy, and miss CSS features like `backdrop-filter`, complex shadows, and web fonts. The `drawElementImage` API uses the browser's own compositor, so:
- **Pixel-perfect** — every CSS feature is supported because the browser renders it natively
- **GPU-accelerated** — captures at 60fps, fast enough for real-time animation
- **Live content** — the HTML can animate, scroll, and change between captures
- **Multiple captures simultaneously** — no nesting restrictions, multiple `<canvas layoutsubtree>` elements can capture different content in the same composition
## Feature detection
Always feature-detect before using the API. Compositions should fall back gracefully for browsers without the flag enabled.
```javascript
function isSupported() {
var tc = document.createElement("canvas");
if (!("layoutSubtree" in tc)) return false;
tc.setAttribute("layoutsubtree", "");
var ctx = tc.getContext("2d");
return ctx && typeof ctx.drawElementImage === "function";
}
if (isSupported()) {
ctx.drawElementImage(element, 0, 0, w, h);
} else {
// Fallback: draw text directly on canvas, use static image, etc.
}
```
## Re-capturing every frame
For animated content (scrolling, transitions, counters), call `drawElementImage` inside your render loop to update the texture every frame:
```javascript
function render() {
// Update HTML state
scrollContainer.style.transform = "translateY(-" + scrollOffset + "px)";
counterEl.textContent = Math.round(currentValue);
// Re-capture
ctx.clearRect(0, 0, W, H);
ctx.drawElementImage(htmlElement, 0, 0, W, H);
texture.needsUpdate = true;
// Render 3D scene with updated texture
renderer.render(scene, camera);
}
```
## Catalog blocks
Install all HTML-in-Canvas blocks at once:
```bash
npx hyperframes add html-in-canvas
```
Or install individually:
| Block | Description | Install |
|-------|-------------|---------|
| [Liquid Glass](/catalog/blocks/vfx-liquid-glass) | Voronoi glass fracture with parallax reveal | `npx hyperframes add vfx-liquid-glass` |
| [iPhone & MacBook](/catalog/blocks/vfx-iphone-device) | Real 3D GLTF devices with live HTML screens | `npx hyperframes add vfx-iphone-device` |
| [Text Cursor](/catalog/blocks/vfx-text-cursor) | Dramatic text reveal with chromatic shadows | `npx hyperframes add vfx-text-cursor` |
| [Portal](/catalog/blocks/vfx-portal) | Dimension breach with volumetric light | `npx hyperframes add vfx-portal` |
| [Shatter](/catalog/blocks/vfx-shatter) | HTML shatters into glass fragments | `npx hyperframes add vfx-shatter` |
| [Magnetic](/catalog/blocks/vfx-magnetic) | Magnetic field particle visualization | `npx hyperframes add vfx-magnetic` |
| [Liquid Background](/catalog/blocks/vfx-liquid-background) | Organic liquid simulation | `npx hyperframes add vfx-liquid-background` |
## Rendering
HyperFrames enables the Chrome flag automatically during rendering. No special configuration needed:
```bash
npx hyperframes render --output my-video.mp4
```
For Docker renders, the flag is also enabled automatically inside the container.