Skip to main content
Version: Next

Renderers

The core engine does not draw by itself. It delegates drawing, image loading, event binding, resize behavior, and platform-specific output to an injected renderer.

import { CanvasTileEngine } from "@canvas-tile-engine/core";
import { RendererCanvas } from "@canvas-tile-engine/renderer-canvas";

const engine = new CanvasTileEngine(wrapper, config, new RendererCanvas(), { x: 0, y: 0 });

Official Renderers

RendererPackageTargetNotes
RendererCanvas@canvas-tile-engine/renderer-canvasBrowser Canvas2DDefault web renderer. Full primitive support, DOM events, high-DPI sizing, static offscreen caches.
RendererWebGL@canvas-tile-engine/renderer-webglBrowser WebGLGPU batches rects, circles, images, lines, paths, and grids. Text and custom draw functions use a 2D overlay.
RendererSkia@canvas-tile-engine/renderer-skiaReact Native SkiaUsed through @canvas-tile-engine/react-native; records frames as Skia pictures.
RendererServer@canvas-tile-engine/renderer-serverNode.jsHeadless output to PNG, JPEG, or WebP Buffer. No DOM events.

Switching Renderers

Canvas2D to WebGL usually only changes the renderer instance:

import { RendererCanvas } from "@canvas-tile-engine/renderer-canvas";
import { RendererWebGL } from "@canvas-tile-engine/renderer-webgl";

new CanvasTileEngine(wrapper, config, new RendererCanvas());
new CanvasTileEngine(wrapper, config, new RendererWebGL());

React uses the same pattern:

import { CanvasTileEngine } from "@canvas-tile-engine/react";
import { RendererWebGL } from "@canvas-tile-engine/renderer-webgl";

<CanvasTileEngine engine={engine} config={config} renderer={new RendererWebGL()} />;

Feature Notes

CapabilityCanvas2DWebGLSkiaServer
Rect/circle/image/text/line/path/gridYesYesYesYes
Browser mouse/touch/wheel eventsYesYesNoNo
Native touch eventsNoNoVia React Native hostNo
Static cache behaviorOffscreen canvas cacheDelegates to dynamic batched drawingSkPicture cacheOffscreen canvas cache
Custom draw contextCanvasRenderingContext2D2D overlay contextSkCanvasSKRSContext2D
Image typeHTMLImageElementHTMLImageElement / TexImageSourceSkImage@napi-rs/canvas Image
WebGL overlay ordering

RendererWebGL draws geometry on the WebGL canvas and text/custom drawing/debug/coordinate overlay on a stacked 2D canvas. Text and custom draw functions therefore composite above WebGL primitives even when their layer number is lower. Ordering within each surface is still layer-based.

Renderer Options

RendererCanvas and RendererWebGL take an optional options object:

OptionTypeDefaultDescription
crossOrigin"anonymous" | "use-credentials" | null"anonymous"crossOrigin attribute for images loaded through engine.images. null drops the attribute, so images served without an Access-Control-Allow-Origin header still load.
// Tiles from a CDN that does not send CORS headers
new CanvasTileEngine(wrapper, config, new RendererCanvas({ crossOrigin: null }));

WebGL needs CORS-clean images to upload them as textures, so crossOrigin: null there causes cross-origin images to be skipped at draw time. See Image Loader.

RendererServer takes pixelRatio; see Server Rendering. RendererSkia takes no options.

Renderer Interface

Renderers implement IRenderer<TMount, TImage> from @canvas-tile-engine/core.

interface IRenderer<TMount = HTMLDivElement, TImage = HTMLImageElement> {
init(deps: RendererDependencies<TMount>): void;
render(): void;
resize(width: number, height: number): void;
resizeWithAnimation(width: number, height: number, durationMs: number, onComplete?: () => void): void;
destroy(): void;
getDrawAPI(): IDrawAPI<TImage>;
getImageLoader(): IImageLoader<TImage>;
setupEvents(): void;
}
Reduced motion in resizeWithAnimation

The engine forwards durationMs unchanged — reduced motion is applied once, by whatever animates. A renderer that animates through the shared AnimationController gets that for free by passing deps.config as the controller's motion policy; a renderer that animates by other means must consult deps.config.getReducedMotion() itself.

The draw API is platform-agnostic and returns DrawHandle values that can be removed later.

interface IDrawAPI<TImage = HTMLImageElement> {
addDrawFunction(
fn: (ctx: unknown, coords: Coords, config: Required<CanvasTileEngineConfig>) => void,
layer?: number,
): DrawHandle;
drawRect(items: Rect | Rect[], layer?: number): DrawHandle;
drawCircle(items: Circle | Circle[], layer?: number): DrawHandle;
drawLine(items: Line | Line[], style?: LineStyle, layer?: number): DrawHandle;
drawText(items: Text | Text[], layer?: number): DrawHandle;
drawImage(items: ImageItem<TImage> | ImageItem<TImage>[], layer?: number): DrawHandle;
drawPath(items: PathItem[], layer?: number): DrawHandle;
drawGridLines(cellSize: number, style: { lineWidth: number; strokeStyle: string }, layer?: number): DrawHandle;
drawStaticRect(items: Rect[], cacheKey: string, layer?: number): DrawHandle;
drawStaticCircle(items: Circle[], cacheKey: string, layer?: number): DrawHandle;
drawStaticImage(items: ImageItem<TImage>[], cacheKey: string, layer?: number): DrawHandle;
removeDrawHandle(handle: DrawHandle): void;
clearLayer(layer: number): void;
clearAll(): void;
clearStaticCache(cacheKey?: string): void;
}

Choosing A Renderer

Start with RendererCanvas unless you already know you need another backend.

  • Use RendererCanvas for browser apps, editors, docs, and medium-sized scenes.
  • Use RendererWebGL when dynamic geometry or image layers are too heavy for Canvas2D.
  • Use @canvas-tile-engine/react-native with RendererSkia for native mobile maps.
  • Use renderToBuffer or RendererServer for OG images, thumbnails, snapshot tests, and pre-rendered assets.