Three.js r184 lands HTMLTexture: live DOM as a 3D surface
Three.js r184 shipped this week. The headline is a new HTMLTexture class that lets you point Three.js at any live HTML element and use it as a texture on a mesh. Buttons stay clickable, inputs stay focusable, CSS animations keep ticking, and the browser handles accessibility and IME the way it always does.
Chrome's official walkthrough of the HTML-in-Canvas API that backs Three.js HTMLTexture
HTMLTexture, in one paragraph
HTMLTexture wraps the Chrome 146 HTML-in-Canvas API. You pass it a DOM element. Three.js sets layoutsubtree on the canvas, parents the element into the renderer's DOM, and projects its rasterized output into a THREE.Texture you can sample from any material on WebGL 2 or WebGPU. The official examples (webgl_materials_texture_html and webgpu_materials_texture_html) include formatted text, images, SVG, a working input, and a button.
In practice this means React components, design system widgets, video players, charts, or full embedded pages can sit on a curved screen, a billboard, or a UI panel inside a Three.js scene without the usual "rasterize to a canvas, then forget about interactivity" tradeoff.
The catch
The feature only lights up where the underlying API exists. Right now that is Chromium 146 and above with the chrome://flags/#canvas-draw-element flag flipped on, which means production sites still need a fallback. The PR keeps the door open by making HTMLTexture fail gracefully when the API is missing, but you will want to feature-detect before promising clients a fully interactive Three.js dashboard.
Safari and Firefox have not signaled timelines. Given that WebGPU itself only just reached baseline across all three browsers in late 2025, HTML-in-Canvas will likely take a similar two-year arc.
A community demo of HTML-in-Canvas before the Three.js wrapper landed
The other shoe: zero per-frame allocations
The less flashy but arguably more important change is in the renderer's hot path. Before r184, rendering 1,000 meshes at 60 FPS could allocate 240,000 to 500,000 throwaway objects per second. The garbage collector then had to deal with that on every long-running scene. r184 eliminates those allocations on the core render loop. Long-running WebXR sessions, dense construction viewers, and any scene that lives past the first 30 seconds gets a real frame-time win for free.
Smaller things worth flagging
AnimationAction now preserves interpolant settings on creation, so a timeScale reversal no longer jumps. AudioLoader no longer races the loading manager. BatchedMesh and InstancedMesh stop throwing from getColorAt when colors haven't been set. The deprecated WebGL instancing render paths are gone for good. Examples gained an SSGI ball pool, volumetric clouds on the 3D Tiles loader, and banking on the roller-coaster camera.
What we'd do with it
For Cinevva, the most interesting use case is on-canvas UI for the reels player and creator tools. Right now we composite HTML over Three.js. With HTMLTexture, the chat overlay, the asset browser, and the prompt input can all live on geometry inside the scene without losing keyboard navigation. As soon as the API ships unflagged in Chrome stable, we'll wire up a feature-flagged variant.