My App
BoardKitFrontend

Rendering Engine

Canvas2DRenderer, InputPipeline, viewport utilities, and element renderers

The rendering engine powers the whiteboard canvas with dual-layer rendering and normalized input processing.

Canvas2DRenderer

The main renderer manages two stacked <canvas> elements:

  • Static layer — Renders all elements. Re-renders only when the scene changes (nonce invalidation). Two-pass rendering: non-connectors first, then connectors on top.
  • Interactive layer — Renders every animation frame for cursors, selections, previews, alignment guides, and grid.
class Canvas2DRenderer {
    constructor();
    mount(container: HTMLElement): void;
    destroy(): void;
    requestRender(): void;
    toDataURL(type?: string, quality?: number): string;
}
MethodDescription
mountAttaches two canvas elements to the given container
destroyRemoves canvases and stops animation loop
requestRenderMarks the static layer as dirty for next frame
toDataURLExports the static layer as a data URL (used by useExport)

InputPipeline

Translates raw DOM PointerEvents into normalized InputEvents that feed into the active tool.

class InputPipeline {
    constructor(canvas: HTMLCanvasElement);
    setViewport(viewport: ViewportState): void;
    setTool(tool: Tool): void;
    destroy(): void;
}

The pipeline:

  1. Listens for pointerdown, pointermove, pointerup, pointercancel on the canvas
  2. Converts screen coordinates to world coordinates using the current viewport
  3. Extracts pressure, tilt, button, and modifier state
  4. Feeds the normalized InputEvent into the active tool
  5. Applies the tool's resulting mutations and previews

Viewport Utilities

interface ViewportState {
    offset: Point;
    zoom: number;
}

const MIN_ZOOM = 0.1;
const MAX_ZOOM = 5.0;

function createViewport(): ViewportState;
function screenToWorld(point: Point, viewport: ViewportState): Point;
function worldToScreen(point: Point, viewport: ViewportState): Point;
function applyViewportTransform(ctx: CanvasRenderingContext2D, viewport: ViewportState): void;
function zoomToPoint(viewport: ViewportState, point: Point, zoomDelta: number): ViewportState;
FunctionDescription
createViewportCreates a default viewport (zoom: 1, offset: 0,0)
screenToWorldConverts screen pixel coordinates to world coordinates
worldToScreenConverts world coordinates to screen pixel coordinates
applyViewportTransformApplies the viewport transform to a canvas 2D context
zoomToPointZooms in/out centered on a specific point (smooth zoom-to-cursor)

Element Renderers

Each element type has a dedicated renderer:

function renderStroke(ctx: CanvasRenderingContext2D, element: StrokeElement): void;
function renderShape(ctx: CanvasRenderingContext2D, element: ShapeElement): void;
function renderText(ctx: CanvasRenderingContext2D, element: TextElement): void;
function renderImage(ctx: CanvasRenderingContext2D, element: ImageElement): void;
function renderStickyNote(ctx: CanvasRenderingContext2D, element: StickyNoteElement): void;
function renderConnector(ctx: CanvasRenderingContext2D, element: ConnectorElement, scene: SceneState): void;

Rendering Details

RendererNotes
renderStrokeDraws polyline with StrokeStyle; supports pressure-based variable width
renderShapeSupports rectangle, ellipse, line, arrow, triangle with fill and stroke
renderTextMulti-line text with word wrap, alignment, and font styling
renderImageRenders from loaded Image objects; shows placeholder while loading
renderStickyNoteColored rectangle with rounded corners + centered text
renderConnectorLine between two element anchors with optional arrow heads

Layer Renderers

function renderStaticLayer(ctx: CanvasRenderingContext2D, scene: SceneState): void;
function renderElement(ctx: CanvasRenderingContext2D, element: Element): void;
function renderInteractiveLayer(ctx: CanvasRenderingContext2D, state: BoardStoreState): void;
function renderGrid(ctx: CanvasRenderingContext2D, viewport: ViewportState, config: GridConfig): void;
function renderAlignmentGuides(ctx: CanvasRenderingContext2D, guides: AlignmentGuide[]): void;

Gesture Recognition

The gesture system normalizes mouse, touch, and pen input:

class GestureRecognizer {
    constructor();
    process(event: PointerEvent): GestureTarget;
}

class MouseHandler { /* ... */ }
class TouchHandler { /* ... */ }
class PenHandler   { /* ... */ }
type GestureType = 'tap' | 'drag' | 'pinch' | 'longPress';
type GestureState = 'possible' | 'began' | 'changed' | 'ended' | 'cancelled';

The PenHandler extracts additional pressure and tilt data from stylus input for pressure-sensitive drawing.

On this page