Publishing gate

Run the WebGL scene health check

Use the checklist to score assets, camera behavior, rendering, mobile performance, loading states, and fallback content before publishing.

Run Scene Check Compare lab tools

Export is a product feature

A screenshot button sounds small, but users rely on it for design reviews, bug reports, thumbnails, and documentation. If the export is blank or stale, the whole tool feels unreliable. Treat screenshot export as part of the product, not a console trick.

The export should have a clear filename, a known background choice, and a predictable resolution. If the user changes the scene, render a fresh frame immediately before creating the blob.

Prefer blobs for downloads

Data URLs are convenient for quick tests but can become large strings. `canvas.toBlob` gives you a Blob that can be downloaded through an object URL. It is a better fit for a real button and keeps memory usage easier to reason about.

If the tool supports both transparent and solid backgrounds, apply that setting before render. A user should not have to guess why their exported PNG looks different from the preview.

Canvas does not include the DOM

Labels, buttons, and HTML overlays outside the canvas will not appear in canvas export. If labels matter, render them inside WebGL, use a separate DOM capture pipeline, or tell users that the export is scene-only. Clear limitation text is better than a surprising file.

For a Three.js lab, scene-only export is often exactly what users need: a clean asset preview they can drop into a document or design board.

Make export state visible

A tiny status line that says 'PNG saved' or 'Export failed' makes the feature feel finished. Avoid browser alerts. Keep the status near the export button and use plain language. If export fails because the canvas is tainted by cross-origin assets, explain that the source image server must allow canvas use.

The boring edge cases are the ones people remember. Handle them with dignity and the tool feels much more trustworthy.

Export from a scene with known canvas content

The examples gallery supplies small scenes for checking render timing and the difference between WebGL pixels and surrounding DOM.

  • Canvas contains a rendered frame
  • DOM labels are not part of canvas output
  • Filename and MIME type are explicit
Open a sample scene

Render immediately before capture

Capturing an old or cleared drawing buffer can produce an empty image.

Fragile pattern
downloadLink.href = renderer.domElement.toDataURL();
downloadLink.click();
Testable pattern
renderer.render(scene, camera);
renderer.domElement.toBlob((blob) => {
  if (!blob) return showExportError();
  downloadBlob(blob, 'threejs-scene.png');
}, 'image/png');

Export contract

The output should describe exactly what the browser is asked to create.

PropertyRecorded choice
Formatimage/png
Capture sourceWebGL canvas only
Frame timingExplicit render before toBlob
Transparent backgroundOnly when renderer alpha and clear alpha allow it

Verify a screenshot button

Test visible output, failure feedback, and repeated use.

  1. Render the intended camera frame immediately before capture.
  2. Create a Blob and reject a null result with an inline message.
  3. Download with a deterministic filename and revoke the object URL.
  4. Repeat after resize and after changing the scene background.

Export button handler

async function downloadCanvasPng(canvas, renderer, scene, camera) {
  renderer.render(scene, camera);
  const blob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/png'));
  if (!blob) throw new Error('Canvas export failed');

  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = `threejs-export-${Date.now()}.png`;
  link.click();
  URL.revokeObjectURL(url);
}

Sources and further reading

Update record

  • : Rewritten around a reproducible demo, explicit test record, code comparison, and known platform limits.

Related guides