Skip to content

synthesiseng/prism

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

78 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Prism

Prism

npm alpha version npm downloads node version CI status license

Docs

HTML-in-Canvas is coming to browsers. Past the cool demo, it gets messy. Prism handles the lifecycle so you don't have to.

Prism is a runtime for managed HTML surfaces inside canvas applications.

Author visual surfaces with HTML/CSS/SVG. Compose them inside Canvas 2D workflows. Ship data visualizations, design tools, generative art, interactive editors and more.

Note: Native mode requires Chrome Canary with chrome://flags/#canvas-draw-element enabled. Prism falls back to a compatibility backend in unsupported browsers.


How It Works

Your app owns the scene, drawing model, animation loop, and state. Prism owns the lifecycle for DOM-authored canvas surfaces — registration, bounds, paint, invalidation, readiness, coordinate helpers, and cleanup.

Built With Prism

  • Atlantic — North Atlantic tropical cyclone tracker, 2000–2025
  • Composer — OG image generator
  • Atelier — Generative type art

Installation

pnpm add @synthesisengineering/prism

Agent Skill

Using an AI coding agent? Install the Prism runtime skill:

npx skills add synthesiseng/prism --skill prism-runtime

The skill teaches agents the Prism runtime contract:

  • import from the package root: @synthesisengineering/prism
  • register HTML/CSS/SVG DOM nodes as surfaces
  • draw surfaces inside onPaint()
  • wait for document.fonts.ready and runtime.paintOnce() before export
  • avoid html2canvas, dom-to-image, raw drawElementImage(), and deep imports

Source: skills/prism-runtime/SKILL.md.

Quickstart

import { CanvasRuntime } from "@synthesisengineering/prism";

const runtime = new CanvasRuntime(canvas, { backend: "auto" });
runtime.start();

Register surfaces when you need them:

const surface = runtime.registerSurface(element, {
  bounds: { x: 0, y: 0, width: 1200, height: 630 }
});

runtime.onPaint(({ drawSurface }) => {
  drawSurface(surface);
});

Export Readiness

Use paintOnce() to wait for one complete paint pass before exporting:

await document.fonts.ready;
await runtime.paintOnce();

const blob = await new Promise<Blob | null>((resolve) => {
  canvas.toBlob(resolve, "image/png");
});

Backend Modes

Prism prefers native HTML-in-Canvas and falls back automatically:

new CanvasRuntime(canvas, { backend: "auto" }); // default
new CanvasRuntime(canvas, { backend: "native" }); // native only
new CanvasRuntime(canvas, { backend: "fallback" }); // compatibility only
if (runtime.backendKind !== "native") {
  console.warn("Compatibility mode is lower fidelity.");
}

The fallback backend is lower fidelity. Native is the target.

Coordinate Spaces

Surface bounds and input coordinates use CSS pixels. Direct drawing inside onPaint() uses canvas backing-store pixels.

Use runtime helpers when aligning manual canvas drawing with surface coordinates:

runtime.onPaint(({ ctx, drawSurface }) => {
  drawSurface(surface);

  const size = runtime.cssLengthToCanvasPixels(24);
  ctx.fillRect(0, 0, size, size);
});

Helpers: clientToCanvasPoint() · cssLengthToCanvasPixels() · cssPointToCanvasPixels()

Surface Lifecycle

// Register
const surface = runtime.registerSurface(element, {
  bounds: { x: 0, y: 0, width: 320, height: 180 }
});

// Update bounds
surface.setBounds({ x: 24, y: 32, width: 360, height: 220 });

// Remove
runtime.unregisterSurface(surface);
// or
surface.dispose();

// Destroy runtime
runtime.destroy();

API

import { CanvasRuntime } from "@synthesisengineering/prism";
import type {
  CanvasBackendKind,
  CanvasBackendPreference,
  CanvasPoint,
  CanvasRuntimeOptions,
  CanvasSurface,
  PaintHandler,
  SurfaceBoundsInput,
  SurfaceOptions,
  UpdateHandler
} from "@synthesisengineering/prism";

CanvasRuntime

new CanvasRuntime(canvas, options);

Options: backend: "auto" | "native" | "fallback"

Properties: canvas · width · height · pixelRatio · backendKind

Methods: registerSurface() · unregisterSurface() · onUpdate() · onPaint() · invalidate() · paintOnce() · start() · stop() · destroy() · clientToCanvasPoint() · cssLengthToCanvasPixels() · cssPointToCanvasPixels()

CanvasSurface

Returned by registerSurface(). Do not construct directly.

Properties: element · isDisposed

Methods: getBounds() · setBounds() · dispose()

Considerations

  • Native HTML-in-Canvas is experimental.
  • Fallback mode is compatibility-only, lower fidelity, and not equivalent to native HTML rendering.
  • onPaint() and onUpdate() are additive. Each call registers another handler; it does not replace previous handlers.
  • invalidate() schedules another Prism-owned paint pass when app-owned state changes outside Prism APIs.
  • paintOnce() works without start(). It waits for one runtime-owned paint pass and does not export image data itself.
  • Destroying a runtime disposes its registered surfaces. Disposed surfaces report isDisposed === true; getBounds() and setBounds() throw.
  • Surface bounds and input coordinates use CSS pixels.
  • Direct ctx drawing uses canvas backing-store pixels.
  • Undrawn surfaces are inactive for pointer and focus handling until they are drawn again.

Limitations

  • Native mode depends on browser support for HTML-in-Canvas.
  • The fallback backend is lower fidelity than native HTML rendering.
  • Prism v1 is 2D-first.
  • WebGL/WebGPU integrations are future-facing.

Development

pnpm install
pnpm typecheck
pnpm test
pnpm e2e
pnpm lint
pnpm build

Platform Credit

Prism is built around the HTML-in-Canvas proposal and related WICG standards work. All credit for the underlying platform capability goes to the proposal authors and the WICG.

License

MIT © Synthesis

About

Native-first HTML-in-Canvas runtime for managed DOM surfaces in canvas applications.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors