Skip to content

synthesiseng/prism

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

78 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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