Skip to content

Plugin: Canvas

CanvasPlugin (id 'canvas') mounts a <canvas> on the player container and runs a shared requestAnimationFrame loop that every renderer registered with addRenderer() draws into on the same frame, instead of each visualizer running its own RAF loop.

Options

OptionTypeDefaultPurpose
mountstring | HTMLElementplayer containerWhere the canvas is appended.
width / heightnumberautoOmit both to track the mount element via ResizeObserver.
fpsnumber60Render loop target rate.
pixelRationumberdevicePixelRatio (or 1)Backing-store scale for crisp output on high-DPI screens.
compositeMode'clear' | 'composite''clear'Whether the canvas clears before each frame or accumulates.
pointerEvents'none' | 'auto''none'Whether the canvas intercepts pointer events.

Events

mounted ({ width, height }), resized ({ width, height }), frame ({ deltaMs, time }).

Rules & restrictions

  • Opt-in by design: without this plugin registered, no canvas is created and no animation frame is ever requested, a player that doesn’t use visualization pays zero rendering cost.
  • canvas() throws a StateError (plugin:canvas/not-mounted) if called before use() has run.
  • context() throws a BrowserPolicyError (core:policy/canvas2dUnsupported) if the environment can’t produce a 2D context.

How to extend it

Register a renderer directly for a one-off effect, or build on Visualization for anything that needs frequency/waveform data too.

TypeScript
import nmplayer from '@nomercy-entertainment/nomercy-video-player'; // nomercy-music-player's factory works identically
import { CanvasPlugin } from '@nomercy-entertainment/nomercy-player-core';

const player = nmplayer('player');

player.addPlugin(CanvasPlugin, { fps: 30 });
await player.ready();

const canvas = player.getPlugin(CanvasPlugin);
const stop = canvas!.addRenderer((ctx, deltaMs) => {
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 10, 10);
});
// stop() unregisters this renderer without touching the shared loop.

Next steps