API Reference

Canvas Tile Engine API

Every public class, method, type, hook, and component across the 7 packages — generated from the TypeScript declarations.

@canvas-tile-engine/core

npm

The renderer-agnostic engine: camera, coordinate math, gestures, layers, and the draw API. Every other package builds on core.

Classes

CanvasTileEngine

class

The core engine. Wires the camera, config, renderer, events, and draw helpers together. Generic over the mount target and image type so the same class powers DOM, React Native, and server renderers.

class CanvasTileEngine<TMount = HTMLDivElement, TImage = HTMLImageElement> {
  constructor(
    canvasWrapper: TMount,
    config: CanvasTileEngineConfig,
    renderer: IRenderer<TMount, TImage>,
    center?: Coords
  )

  canvasWrapper: TMount
  canvas: HTMLCanvasElement | undefined
  get images(): IImageLoader<TImage>
}

Parameters

canvasWrapperTMountMount element containing a canvas child (a div on the web).
configCanvasTileEngineConfigInitial engine configuration.
rendererIRenderer<TMount, TImage>Renderer implementation, e.g. new RendererCanvas().
center?CoordsInitial center in world space. Default { x: 0, y: 0 }.
Example
import { CanvasTileEngine } from "@canvas-tile-engine/core";
import { RendererCanvas } from "@canvas-tile-engine/renderer-canvas";

const engine = new CanvasTileEngine(
  document.getElementById("map-wrapper"),
  { scale: 50, size: { width: 800, height: 600 } },
  new RendererCanvas(),
  { x: 0, y: 0 }
);

engine.drawGridLines(1);
engine.render();

SpriteSheet

class

Grid-based spritesheet frame calculator. Maps (col, row) or linear frame indices to pixel source rectangles inside a sheet image. Pure calculation — holds no image reference, so it works with every renderer.

class SpriteSheet {
  constructor(options: SpriteSheetOptions)
  frame(col: number, row: number): SpriteRect
  frameByIndex(index: number): SpriteRect
  framesInRow(row: number, startCol: number, endCol: number): SpriteRect[]
}
Example
const sheet = new SpriteSheet({ frameWidth: 32, frameHeight: 32, columns: 5 });
engine.drawImage({ x: 5, y: 3, img, sprite: sheet.frame(3, 0) });

SpriteAnimator

class

Plays a SpriteAnimation by invoking a callback whenever the current frame changes. The internal rAF loop only fires when the frame index advances, so renders happen at the animation's fps, not at 60fps.

class SpriteAnimator {
  constructor(animation: SpriteAnimation)
  start(onFrame: (frame: SpriteRect, index: number) => void, onComplete?: () => void): void
  stop(): void
  isRunning(): boolean
  frameIndexAt(elapsedMs: number): number
  frameAt(elapsedMs: number): SpriteRect
}
Example
const animator = new SpriteAnimator({ frames: sheet.framesInRow(0, 0, 4), fps: 8 });
animator.start((frame) => {
  item.sprite = frame;
  engine.render();
});

SpatialIndex

class

Spatial indexing wrapper using an R-Tree for fast viewport queries. Used internally for viewport culling of large item sets (500+); exposed for advanced custom querying.

class SpatialIndex<T extends SpatialItem> {
  load(items: T[]): void
  query(minX: number, minY: number, maxX: number, maxY: number): T[]
  clear(): void
  static fromArray<T extends SpatialItem>(items: T[]): SpatialIndex<T>
}

Config

class

Normalizes and stores engine configuration with safe defaults. get() returns a deeply frozen snapshot — do not mutate it. Mostly used internally; renderers receive it via RendererDependencies.

class Config {
  constructor(config: CanvasTileEngineConfig)
  get(): Readonly<Required<CanvasTileEngineConfig>>
  updateEventHandlers(handlers: Partial<EventHandlers>): void
  updateBounds(bounds: Bounds): void
}

CoordinateTransformer

class

Transforms coordinates between world space (tile indices) and screen space (pixels) using the active camera's offset and scale.

class CoordinateTransformer {
  constructor(camera: ICamera)
  worldToScreen(worldX: number, worldY: number): Coords
  screenToWorld(screenX: number, screenY: number): Coords
}

ViewportState

class

Holds the mutable viewport size for runtime changes (resize, layout) and tracks device pixel ratio for HiDPI/Retina rendering.

class ViewportState {
  constructor(width: number, height: number)
  getSize(): { width: number; height: number }
  setSize(width: number, height: number): void
  get dpr(): number
  updateDpr(): void
}

GestureProcessor

class

Handles gesture logic (click, hover, drag, zoom, pinch) independent of DOM/platform. Renderers feed it normalized pointer input; it performs the camera math and fires the engine callbacks.

class GestureProcessor {
  handleClick(pointer: NormalizedPointer): void
  handlePointerDown(pointer: NormalizedPointer): void
  handlePointerMove(pointer: NormalizedPointer): void
  handlePointerUp(pointer: NormalizedPointer): void
  handleWheel(pointer: NormalizedPointer, deltaY: number): void
  handleTouchStart(pointers: NormalizedPointer[]): void
  handleTouchMove(pointers: NormalizedPointer[]): void
  handleTouchEnd(remaining: NormalizedPointer[], changed?: NormalizedPointer): void
  get dragging(): boolean
  get pinching(): boolean
}

AnimationController

class

Manages smooth animations for camera movements and canvas resizing, including animation frame scheduling and cleanup. Backs engine.goCoords and engine.resize.

class AnimationController {
  animateMoveTo(targetX: number, targetY: number, durationMs?: number, onComplete?: () => void): void
  animateResize(targetWidth: number, targetHeight: number, durationMs: number | undefined, onApplySize: (w: number, h: number, center: Coords) => void, onComplete?: () => void): void
  cancelMove(): void
  cancelResize(): void
  cancelAll(): void
  isAnimating(): boolean
}

Engine Methods

render()

method

Render a frame using the active renderer. Call after registering or changing draw items.

render(): void

destroy()

method

Tear down listeners and observers. Call when removing the engine to avoid leaks.

destroy(): void

resize()

method

Manually update canvas size while keeping the view centered. Animates over durationMs (default 500); pass 0 for an instant resize.

resize(width: number, height: number, durationMs?: number, onComplete?: () => void): void

Parameters

widthnumberNew canvas width in pixels.
heightnumberNew canvas height in pixels.
durationMs?numberAnimation duration in ms (default 500).
onComplete?() => voidFired when the resize animation completes.

getSize()

method

Current canvas size in pixels.

getSize(): { width: number; height: number }

Returns { width, height } in pixels.

getScale()

method

Current canvas scale (pixels per world unit).

getScale(): number

setScale()

method

Set the canvas scale directly, clamped to min/max bounds. Throws ConfigValidationError if the scale is not a positive finite number.

setScale(newScale: number): void

zoomIn()

method

Zoom in by a factor (default 1.5), centered on the viewport.

zoomIn(factor?: number): void

zoomOut()

method

Zoom out by a factor (default 1.5), centered on the viewport.

zoomOut(factor?: number): void

getConfig()

method

Snapshot of the current normalized config.

getConfig(): Required<CanvasTileEngineConfig>

getCenterCoords()

method

Center coordinates of the map in world space.

getCenterCoords(): Coords

getVisibleBounds()

method

The visible world coordinate bounds of the viewport, floored/ceiled to whole cells — i.e. which tiles are on screen.

getVisibleBounds(): { minX: number; maxX: number; minY: number; maxY: number }
Example
const bounds = engine.getVisibleBounds();
// { minX: 0, maxX: 10, minY: 0, maxY: 10 }

// Random placement within the visible area
const x = bounds.minX + Math.floor(Math.random() * (bounds.maxX - bounds.minX));

updateCoords()

method

Set center coordinates from outside; the camera adjusts instantly. Throws if coordinates are not finite numbers.

updateCoords(newCenter: Coords): void

goCoords()

method

Smoothly move the camera center to target coordinates over the given duration (default 500ms; 0 = instant).

goCoords(x: number, y: number, durationMs?: number, onComplete?: () => void): void
Example
engine.goCoords(25, 40, 800, () => console.log("arrived"));

setEventHandlers()

method

Enable or disable specific interactions at runtime — e.g. turn drag off while the user paints.

setEventHandlers(handlers: Partial<EventHandlers>): void
Example
// Painting mode: disable drag, keep hover
engine.setEventHandlers({ drag: false, hover: true });

// Back to pan mode
engine.setEventHandlers({ drag: true });

setBounds()

method

Set or update map boundaries to restrict camera movement. Use Infinity/-Infinity to remove limits on specific axes.

setBounds(bounds: { minX: number; maxX: number; minY: number; maxY: number }): void
Example
// Restrict map to -100..100 on both axes
engine.setBounds({ minX: -100, maxX: 100, minY: -100, maxY: 100 });

// Only limit the X axis
engine.setBounds({ minX: 0, maxX: 500, minY: -Infinity, maxY: Infinity });

clearStaticCache()

method

Clear a static rendering cache by key, or all caches when the key is omitted.

clearStaticCache(cacheKey?: string): void

Draw Methods

drawRect()

method

Draw one or many rectangles in world space. Supports rotation (degrees, clockwise) and per-corner border radius.

drawRect(items: Rect | Rect[], layer?: number): DrawHandle

Parameters

itemsRect | Rect[]Rectangle definitions.
layer?numberLayer order — lower draws first.

Returns DrawHandle for later removal.

Example
engine.drawRect({
  x: 2, y: 3, size: 1,
  style: { fillStyle: "#f59e0b" },
  radius: 4,
});

drawCircle()

method

Draw one or many circles sized in world units.

drawCircle(items: Circle | Circle[], layer?: number): DrawHandle

drawLine()

method

Draw one or many lines between world points.

drawLine(items: Line | Line[], style?: LineStyle, layer?: number): DrawHandle

drawText()

method

Draw one or many texts at world positions. Font size is in world units, so text scales with zoom.

drawText(items: Text | Text[], layer?: number): DrawHandle
Example
engine.drawText({
  x: 0, y: 0,
  text: "Hello",
  size: 1, // 1 tile height
  style: { fillStyle: "black", fontFamily: "Arial" },
});

drawPath()

method

Draw one or many polylines through world points.

drawPath(items: Path | Path[], style?: LineStyle, layer?: number): DrawHandle

drawImage()

method

Draw one or many images scaled in world units. Set the sprite field to draw a sub-region of a spritesheet; supports rotation.

drawImage(items: ImageItem | ImageItem[], layer?: number): DrawHandle
Example
const img = await engine.images.load("/tiles/grass.png");
engine.drawImage({ x: 4, y: 2, img, size: 1 });

drawGridLines()

method

Draw grid lines at the specified cell size in world units.

drawGridLines(cellSize: number, lineWidth?: number, strokeStyle?: string, layer?: number): DrawHandle
Example
engine.drawGridLines(1, 1, "#e4e4e7", 0);

drawStaticRect()

method

Draw rectangles with a pre-rendering cache: items render once to an offscreen canvas, then the visible portion is blitted each frame. Ideal for large static datasets like minimaps.

drawStaticRect(items: Rect[], cacheKey: string, layer?: number): DrawHandle

Parameters

itemsRect[]Rectangle definitions.
cacheKeystringUnique key for this cache, e.g. "minimap-items".
layer?numberLayer order.

drawStaticCircle()

method

Circle variant of drawStaticRect — cached offscreen rendering for static datasets.

drawStaticCircle(items: Circle[], cacheKey: string, layer?: number): DrawHandle

drawStaticImage()

method

Image variant of drawStaticRect — ideal for terrain tiles and static decorations.

drawStaticImage(items: ImageItem[], cacheKey: string, layer?: number): DrawHandle

addDrawFunction()

method

Register a custom draw function for complete rendering control. The context type depends on the renderer (CanvasRenderingContext2D on the web, SkCanvas on Skia).

addDrawFunction(
  fn: (ctx: unknown, coords: Coords, config: Required<CanvasTileEngineConfig>) => void,
  layer?: number
): DrawHandle

removeDrawHandle()

method

Remove a specific draw callback by handle without touching other callbacks on the same layer.

removeDrawHandle(handle: DrawHandle): void

clearLayer()

method

Clear all draw callbacks from a specific layer. Use before redrawing dynamic content to prevent accumulation.

clearLayer(layer: number): void
Example
engine.clearLayer(1);
engine.drawRect(newRects, 1);
engine.render();

clearAll()

method

Clear all draw callbacks from all layers for a complete scene reset.

clearAll(): void

Event Callbacks

onClick

event

Fired when a tile is clicked (mouse or touch tap). coords is in world space, mouse is canvas-relative, client is viewport-relative; raw is exact, snapped is floored to the tile.

onClick?: (coords, mouse, client) => void
// each argument: { raw: Coords; snapped: Coords }
Example
engine.onClick = (coords) => {
  console.log(`Clicked tile: ${coords.snapped.x}, ${coords.snapped.y}`);
};

onRightClick

event

Fired when a tile is right-clicked (long-press on touch platforms).

onRightClick?: (coords, mouse, client) => void
Example
engine.onRightClick = (coords) => {
  showContextMenu(coords.snapped.x, coords.snapped.y);
};

onHover

event

Fired while hovering over tiles.

onHover?: (coords, mouse, client) => void
Example
engine.onHover = (coords) => {
  setHoveredTile(coords.snapped);
};

onMouseDown

event

Fired on mouse/touch down. Useful for starting paint or selection gestures.

onMouseDown?: (coords, mouse, client) => void

onMouseUp

event

Fired on mouse/touch up.

onMouseUp?: (coords, mouse, client) => void

onMouseLeave

event

Fired when the pointer leaves the canvas.

onMouseLeave?: (coords, mouse, client) => void

onCoordsChange

event

Fired when the center coordinates change (pan or zoom).

onCoordsChange?: (coords: Coords) => void
Example
engine.onCoordsChange = (coords) => {
  console.log(`Center: ${coords.x}, ${coords.y}`);
};

onZoom

event

Fired when the zoom level changes — wheel, pinch, or programmatic (setScale / zoomIn / zoomOut).

onZoom?: (scale: number) => void

onDraw

event

Fired after each draw frame, for custom overlay drawing directly on the rendering context.

onDraw?: (ctx, info: { scale: number; width: number; height: number; coords: Coords }) => void
Example
engine.onDraw = (ctx, info) => {
  ctx.fillStyle = "red";
  ctx.fillText(`Scale: ${info.scale}`, 10, 20);
};

onResize

event

Fired when the canvas is resized.

onResize?: () => void

Types

CanvasTileEngineConfig

type

The engine configuration. scale is pixels per world unit; gridAligned snaps center coordinates to cell centers for pixel-perfect alignment; responsive controls how the canvas reacts to container resizes; debug enables an FPS/coords HUD.

type CanvasTileEngineConfig = {
  scale: number
  maxScale?: number
  minScale?: number
  backgroundColor?: string
  gridAligned?: boolean
  size: {
    width: number; height: number
    minWidth?: number; minHeight?: number
    maxWidth?: number; maxHeight?: number
  }
  responsive?: "preserve-scale" | "preserve-viewport" | false
  eventHandlers?: EventHandlers
  bounds?: { minX: number; maxX: number; minY: number; maxY: number }
  coordinates?: {
    enabled?: boolean
    shownScaleRange?: { min: number; max: number }
  }
  cursor?: { default?: string; move?: string }
  debug?: {
    enabled?: boolean
    hud?: { enabled?: boolean; coordinates?: boolean; scale?: boolean; tilesInView?: boolean; fps?: boolean }
    eventHandlers?: { click?: boolean; hover?: boolean; drag?: boolean; zoom?: boolean; resize?: boolean }
  }
}

EventHandlers

type

Which built-in interactions are active. zoom accepts false, true (shorthand for "pointer"), or a ZoomMode.

type EventHandlers = {
  click?: boolean
  rightClick?: boolean
  hover?: boolean
  drag?: boolean
  zoom?: boolean | ZoomMode
  resize?: boolean
}

ZoomMode

type

Anchor point for wheel/pinch zoom: "pointer" zooms toward the cursor or pinch midpoint, "center" toward the canvas center.

type ZoomMode = "pointer" | "center"

Coords

type

A point in world or screen space.

type Coords = { x: number; y: number }

DrawObject

type

Base shape for drawable items. Position and size are in world units; origin controls the anchor point within the cell or the item itself.

type DrawObject = {
  x: number
  y: number
  size?: number
  origin?: { mode?: "cell" | "self"; x?: number; y?: number }
  style?: { fillStyle?: string; strokeStyle?: string; lineWidth?: number }
  rotate?: number            // degrees, positive = clockwise
  radius?: number | number[] // px; single or [tl, tr, br, bl]
}

Rect / Circle

type

Rect is a full DrawObject; Circle drops rotation and radius (circles need neither).

type Rect = DrawObject
type Circle = Omit<DrawObject, "rotate" | "radius">

Text

type

A text item drawn at a world position with zoom-scaled font size.

type Text = Omit<DrawObject, "radius" | "size"> & {
  text: string
  size?: number // font size in world units (scales with zoom), default 1
  style?: {
    fillStyle?: string
    fontFamily?: string
    textAlign?: TextAlign
    textBaseline?: TextBaseline
  }
}

Line / Path

type

A single segment between two world points, and a polyline through many.

type Line = { from: Coords; to: Coords }
type Path = Coords[]
interface LineStyle { strokeStyle?: string; lineWidth?: number }

ImageItem

type

An image to draw. TImage is the platform-specific image handle — HTMLImageElement on the web, SkImage on React Native, Image on the server.

type ImageItem<TImage = HTMLImageElement> = Omit<DrawObject, "style"> & {
  img: TImage
  sprite?: SpriteRect // draw only this sub-region (spritesheet frame)
}

SpriteRect

type

A source rectangle inside a spritesheet image, in sheet pixels. Produced by SpriteSheet, consumed by ImageItem.sprite.

type SpriteRect = { x: number; y: number; w: number; h: number }

SpriteSheetOptions

type

Construction options for SpriteSheet.

interface SpriteSheetOptions {
  frameWidth: number
  frameHeight: number
  columns?: number // required for index-based lookups
  margin?: number  // outer offset from sheet edges (default 0)
  spacing?: number // gap between adjacent frames (default 0)
}

SpriteAnimation

type

A frame-based sprite animation definition, played by SpriteAnimator.

interface SpriteAnimation {
  frames: SpriteRect[]
  fps: number
  loop?: boolean // default true
}

DrawHandle

type

Opaque handle returned by every draw method. Pass to removeDrawHandle to remove that item set.

interface DrawHandle {
  readonly id: symbol
  readonly layer: number
}

Bounds

type

A world-space rectangle used for map limits and visibility queries.

interface Bounds { minX: number; maxX: number; minY: number; maxY: number }

IRenderer

interface

The renderer contract. Implement this to plug a new rendering backend into the engine — this is what makes the engine renderer-agnostic.

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
  // + optional event callback fields (onClick, onHover, onZoom, ...)
}

IDrawAPI

interface

The draw surface each renderer exposes. The engine's draw methods delegate to this.

interface IDrawAPI<TImage = HTMLImageElement> {
  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: Path | Path[], style?: LineStyle, 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
  addDrawFunction(fn, layer?): DrawHandle
  removeDrawHandle(handle: DrawHandle): void
  clearLayer(layer: number): void
  clearAll(): void
  clearStaticCache(cacheKey?: string): void
}

IImageLoader

interface

Platform-agnostic image loader with caching. Available on every engine as engine.images.

interface IImageLoader<TImage = HTMLImageElement> {
  load(src: string, retry?: number): Promise<TImage>
  get(src: string): TImage | undefined
  has(src: string): boolean
  clear(): void
  onLoad(cb: () => void): () => void
}

ICamera

interface

Camera contract used by rendering and coordinate transforms: top-left world position, zoom scale, and the pan/zoom operations.

interface ICamera {
  readonly x: number
  readonly y: number
  readonly scale: number
  pan(dx: number, dy: number): void
  zoom(screenX: number, screenY: number, deltaY: number, bounds: ViewportBounds): void
  zoomByFactor(factor: number, centerX: number, centerY: number): void
  setScale(scale: number): void
  setCenter(center: Coords, viewportWidth: number, viewportHeight: number): void
  getCenter(viewportWidth: number, viewportHeight: number): Coords
  getVisibleBounds(viewportWidth: number, viewportHeight: number): Bounds
  setBounds(bounds: Bounds): void
  adjustForResize(dw: number, dh: number): void
}

RendererDependencies

interface

Dependencies injected into a renderer's init call.

interface RendererDependencies<TMount = HTMLDivElement> {
  wrapper: TMount
  camera: ICamera
  viewport: ViewportState
  config: Config
  transformer: CoordinateTransformer
}

Utilities & Constants

gridToSize()

function

Convert grid-based dimensions to a pixel-based config — say the grid you want, get the size and scale.

function gridToSize(options: {
  columns: number
  rows: number
  cellSize: number
}): Pick<CanvasTileEngineConfig, "size" | "scale"> & { center: Coords }
Example
const {center, ...board} = gridToSize({ columns: 12, rows: 12, cellSize: 50 });
  const config = {
    ...board,
    backgroundColor: "#337426",
  };
// config.size  = { width: 600, height: 600 }
// config.scale = 50
const engine = new CanvasTileEngine(wrapper, config, new RendererCanvas(), center);

DEFAULT_VALUES

constant

Global engine defaults: animation timing, wheel clamping, zoom sensitivity.

const DEFAULT_VALUES: {
  ANIMATION_DURATION_MS: 500
  CELL_CENTER_OFFSET: 0.5
  IMAGE_LOAD_RETRY_COUNT: 1
  MAX_WHEEL_DELTA: 100
  MIN_WHEEL_DELTA: -100
  ZOOM_SENSITIVITY: 0.001
}

SCALE_LIMITS

constant

Default zoom range relative to the configured scale when minScale/maxScale are not set.

const SCALE_LIMITS: {
  MIN_SCALE_MULTIPLIER: 0.5
  MAX_SCALE_MULTIPLIER: 2
}

SIZE_LIMITS

constant

Default canvas size limits when min/max are not configured.

const SIZE_LIMITS: {
  MIN_WIDTH: 100
  MIN_HEIGHT: 100
  MAX_WIDTH: number  // Infinity
  MAX_HEIGHT: number // Infinity
}

RENDER_DEFAULTS

constant

Default rendering values, currently the background color.

const RENDER_DEFAULTS: { BACKGROUND_COLOR: "#ffffff" }