Menu

Rendering System

Relevant source files

The Rendering System is responsible for drawing all visual elements on the Excalidraw canvas. It implements a dual-canvas architecture where a static canvas renders the actual element shapes, and an interactive canvas renders overlays such as selection handles, transform handles, binding highlights, and remote collaborator cursors. The system is designed for performance through viewport culling, render throttling, and memoization.

For information about how elements are stored and managed before rendering, see Scene and Element Collection. For details on font loading and text metrics used during rendering, see Font Management. For information on the visual alignment guides rendered during dragging, see Snapping System.

Dual-Canvas Architecture

Excalidraw uses two overlaid HTML canvas elements to separate static content from interactive overlays. This separation enables performance optimizations where the static canvas only re-renders when elements change, while the interactive canvas updates frequently during user interactions.

Sources: packages/excalidraw/components/canvases/StaticCanvas.tsx1-134 packages/excalidraw/components/canvases/InteractiveCanvas.tsx1-249 packages/excalidraw/scene/Renderer.ts1-158

Canvas Component Lifecycle

Both canvas components are React components that use React.memo with custom comparison functions to prevent unnecessary re-renders. They compare sceneNonce, selectionNonce, and relevant AppState properties to determine if a re-render is needed.

ComponentPurposeRe-render Triggers
StaticCanvasRenders element shapessceneNonce, elementsMap, visibleElements, viewport changes
InteractiveCanvasRenders UI overlaysselectionNonce, sceneNonce, selection changes, collaborator updates

Sources: packages/excalidraw/components/canvases/StaticCanvas.tsx107-131 packages/excalidraw/components/canvases/InteractiveCanvas.tsx219-246

Renderer Class

The Renderer class in packages/excalidraw/scene/Renderer.ts manages the filtering and preparation of elements for rendering. It computes which elements are visible in the current viewport and should be drawn.

Sources: packages/excalidraw/scene/Renderer.ts27-148

Viewport Culling

The getRenderableElements() method is a memoized function that performs viewport culling to reduce the number of elements passed to the rendering pipeline:

  1. Element Filtering: Excludes elements that are currently being edited as text (they are rendered differently)
  2. Viewport Culling: Uses isElementInViewport() to test each element against the current viewport bounds
  3. Result Caching: Memoizes results based on viewport parameters and sceneNonce to avoid redundant calculations

Sources: packages/excalidraw/scene/Renderer.ts70-147

Memoization Strategy

The getRenderableElements() function uses memoization to cache its results. The cache key includes:

  • zoom, offsetLeft, offsetTop, scrollX, scrollY, height, width (viewport parameters)
  • editingTextElement (which element is being edited)
  • newElementId (element being created)
  • sceneNonce (cache invalidation when scene changes)

Sources: packages/excalidraw/scene/Renderer.ts99-148

Static Canvas Rendering

The static canvas renders the actual element shapes using the RoughJS library for hand-drawn aesthetics. This canvas only re-renders when elements change or viewport parameters change.

Sources: packages/excalidraw/renderer/staticScene.ts (referenced but not provided), packages/excalidraw/components/canvases/StaticCanvas.tsx33-75

Static Canvas Component

The StaticCanvas component manages the static canvas element and calls renderStaticScene() in a useEffect hook. Key aspects:

  • Canvas size is set based on appState.width, appState.height, and device pixel ratio
  • Uses isRenderThrottlingEnabled() to optionally throttle renders
  • The canvas is inserted into a wrapper div on first mount
  • Memoized with custom areEqual comparison to prevent unnecessary re-renders

Sources: packages/excalidraw/components/canvases/StaticCanvas.tsx33-134

Relevant AppState for Static Rendering

Sources: packages/excalidraw/components/canvases/StaticCanvas.tsx77-105

Interactive Canvas Rendering

The interactive canvas renders UI overlays that change frequently during user interactions. It does not render the element shapes themselves, only visual feedback for editing operations.

Sources: packages/excalidraw/renderer/interactiveScene.ts728-1330

Interactive Rendering Components

The _renderInteractiveScene() function orchestrates rendering of various interactive overlays:

Render FunctionPurposeWhen Rendered
renderLinearPointHandles()Point handles for lines/arrowsWhen linear element is selected/editing
renderSelectionElement()Drag selection rectangleDuring box selection
renderTextBox()Text element outlineWhen editing text without autoResize
renderBindingHighlight()Binding target highlightWhen isBindingEnabled and suggestedBindings exist
renderFrameHighlight()Frame outline highlightWhen frameToHighlight is set
renderElementsBoxHighlight()Multi-element box highlightWhen elementsToHighlight is set
renderSelectionBorder()Selection border for elementsFor each selected element
renderTransformHandles()Resize/rotate handlesFor selected elements (single or multiple)
renderCropHandles()Image crop handlesWhen croppingElementId is set
renderRemoteCursors()Collaborator cursorsFor each active collaborator
renderSnaps()Snap alignment linesWhen snapLines array has entries

Sources: packages/excalidraw/renderer/interactiveScene.ts728-1330

Linear Element Point Handles

Linear elements (lines, arrows) display point handles when selected. The rendering logic handles:

  • Regular point handles at each point
  • Segment midpoint handles (for adding new points)
  • Different styles for selected vs unselected points
  • Phantom points (midpoints that can be clicked to add points)
  • Overlapping point detection (when polygon endpoint overlaps with first point)

Sources: packages/excalidraw/renderer/interactiveScene.ts434-556

Selection Borders and Transform Handles

Selection borders are rendered with customizable colors for local and remote selections:

  • Local selections use selectionColor (CSS variable --color-selection)
  • Remote selections use collaborator-specific colors
  • Locked elements use a gray dashed border (#ced4da)
  • Multiple selected elements can have individual or grouped selection borders

Transform handles (for resizing and rotating) are rendered as small rectangles or circles at element corners and edges:

  • Single element: shows all appropriate handles based on element type
  • Multiple elements: shows handles on the bounding box of all elements
  • Frames: omit rotation handle when selected with other elements
  • Hidden when text is being edited or image is being cropped

Sources: packages/excalidraw/renderer/interactiveScene.ts558-1105

Remote Collaborator Rendering

Remote collaborators are visualized through:

  1. Remote Cursors: Rendered by renderRemoteCursors() with collaborator-specific colors and usernames
  2. Remote Selection Borders: Elements selected by collaborators have colored borders
  3. Remote Pointer Viewport Coords: Calculated in InteractiveCanvas component before calling render function

Sources: packages/excalidraw/components/canvases/InteractiveCanvas.tsx82-123 packages/excalidraw/renderer/interactiveScene.ts914-983

Relevant AppState for Interactive Rendering

Sources: packages/excalidraw/components/canvases/InteractiveCanvas.tsx184-217

Render Configuration

Both static and interactive rendering accept configuration objects that control rendering behavior:

InteractiveCanvasRenderConfig

This configuration is built in the InteractiveCanvas component by iterating over appState.collaborators and extracting pointer positions, selected elements, and user metadata.

Sources: packages/excalidraw/components/canvases/InteractiveCanvas.tsx82-149

StaticCanvasRenderConfig

Sources: packages/excalidraw/scene/types.ts (referenced but not provided in full)

Performance Optimizations

The rendering system employs several strategies to maintain high performance:

Render Throttling

Both renderStaticScene and renderInteractiveScene have throttled versions:

The throttled versions use throttleRAF() which ensures rendering happens at most once per animation frame (typically 60 FPS).

Sources: packages/excalidraw/scene/Renderer.ts153-154 packages/excalidraw/components/canvases/InteractiveCanvas.tsx152

Memoization

  1. Renderer Memoization: getRenderableElements() is memoized with cache invalidation based on viewport and scene parameters
  2. Component Memoization: Both StaticCanvas and InteractiveCanvas use React.memo() with custom comparison functions
  3. Scene Nonces: sceneNonce and selectionNonce provide efficient cache invalidation without deep object comparisons

Sources: packages/excalidraw/scene/Renderer.ts99-148 packages/excalidraw/components/canvases/StaticCanvas.tsx107-131 packages/excalidraw/components/canvases/InteractiveCanvas.tsx219-246

Viewport Culling

The isElementInViewport() function tests element bounds against viewport dimensions, preventing off-screen elements from being processed by the rendering pipeline. This is particularly important for large scenes with hundreds or thousands of elements.

Sources: packages/excalidraw/scene/Renderer.ts46-68

Shape Caching

RoughJS shape generation is computationally expensive. The rendering system uses a ShapeCache to store pre-generated rough paths, avoiding regeneration on every frame. The cache considers:

  • Element properties (roughness, stroke style, etc.)
  • Zoom level (when shouldCacheIgnoreZoom is false)
  • Random seed for reproducible hand-drawn appearance

Sources: Referenced in architecture but implementation in unreferenced files

Bootstrap and Canvas Setup

The bootstrapCanvas() helper function sets up the canvas rendering context with appropriate dimensions and transformations:

This ensures:

  • High-DPI display support via scale (typically window.devicePixelRatio)
  • Consistent coordinate system across devices
  • Padding around canvas edges

Sources: packages/excalidraw/renderer/interactiveScene.ts748-753

Integration with Other Systems

Font Management Integration

Text rendering requires font metrics for accurate bounding box calculations. The rendering system integrates with the Fonts class (see Font Management) to:

  • Ensure fonts are loaded before rendering text elements
  • Retrieve font metrics for text measurement
  • Handle font fallbacks when fonts fail to load

Snapping System Integration

The interactive canvas renders snap lines computed by the snapping system (see Snapping System):

  • renderSnaps() draws alignment guides when appState.snapLines contains snap data
  • Snap lines are rendered as thin colored lines showing alignment points
  • Only rendered during dragging/resizing operations

Sources: packages/excalidraw/renderer/interactiveScene.ts69

Element Binding Integration

Binding highlights show when an arrow can bind to a shape:

  • renderBindingHighlight() draws a translucent highlight around bindable elements
  • renderBindingHighlightForBindableElement() handles shape-specific highlighting (rectangle, ellipse, diamond)
  • renderBindingHighlightForSuggestedPointBinding() shows circular binding zones at arrow endpoints

Sources: packages/excalidraw/renderer/interactiveScene.ts191-354