nteract elements

ipycanvas

Canvas widget for the ipycanvas library

Renders ipycanvas Canvas widgets. Processes drawing commands sent from Python via the ipycanvas binary protocol and renders them on an HTML <canvas> element.

Features

  • Full drawing protocol - Shapes, paths, text, transforms, batch operations
  • Binary buffer support - Efficient NumPy array transfer for batch drawing
  • Mouse and keyboard events - Forwarded back to the Python kernel
  • Canvas state management - save/restore, style attributes, transforms
  • Styled batch operations - Per-element colors and alpha for bulk drawing

Installation

npx shadcn@latest add @nteract/ipycanvas

New to @nteract? pnpm dlx shadcn@latest registry add @nteract

Copy the files to your project from the registry source.

Requires: Widget Store

Setup

Import the ipycanvas registration module to enable canvas widget rendering. This is separate from the built-in controls and must be opted into:

// Import once to register CanvasModel and CanvasManagerModel
import "@/components/widgets/ipycanvas"

This registers both CanvasModel (visual canvas) and CanvasManagerModel (headless command router) with the widget registry.

Usage

Once registered, ipycanvas widgets render automatically through WidgetView:

import { WidgetStoreProvider } from "@/components/widgets/widget-store-context"
import { WidgetView } from "@/components/widgets/widget-view"

// Register ipycanvas widgets
import "@/components/widgets/ipycanvas"

function App() {
  return (
    <WidgetStoreProvider sendMessage={(msg) => kernel.send(msg)}>
      <WidgetView modelId="canvas-comm-id" />
    </WidgetStoreProvider>
  )
}

Python Example

from ipycanvas import Canvas

canvas = Canvas(width=400, height=300)

# Set styles
canvas.fill_style = "red"
canvas.fill_rect(10, 10, 100, 50)

# Draw shapes
canvas.fill_style = "blue"
canvas.fill_circle(200, 150, 40)

# Draw text
canvas.font = "24px sans-serif"
canvas.fill_style = "black"
canvas.fill_text("Hello!", 150, 50)

canvas

Supported Commands

Drawing

CommandDescription
fill_rect, stroke_rect, clear_rectRectangle operations
fill_circle, stroke_circleCircle operations
fill_arc, stroke_arcArc/pie operations
stroke_lineSingle line segment
fill_polygon, stroke_polygonPolygon from points
fill_text, stroke_textText rendering

Batch Operations

Efficiently draw many shapes from NumPy arrays:

CommandDescription
fill_rects, stroke_rectsMany rectangles
fill_circles, stroke_circlesMany circles
fill_arcs, stroke_arcsMany arcs
stroke_linesConnected line segments
fill_polygons, stroke_polygonsMany polygons
stroke_line_segmentsDisconnected line segments

All batch commands also have styled variants (e.g., fill_styled_rects) that accept per-element colors and alpha values.

Path Operations

CommandDescription
begin_path, close_pathPath lifecycle
move_to, line_toPath points
arc, ellipse, arc_toCurved segments
quadratic_curve_to, bezier_curve_toBezier curves
stroke, fill, clipPath rendering

Transforms and State

CommandDescription
translate, rotate, scaleAffine transforms
transform, set_transform, reset_transformMatrix operations
save, restoreContext state stack

Style Attributes

Set via Python properties like canvas.fill_style = "red":

fill_style, stroke_style, global_alpha, font, text_align, text_baseline, direction, global_composite_operation, line_width, line_cap, line_join, miter_limit, line_dash_offset, shadow_offset_x, shadow_offset_y, shadow_blur, shadow_color, filter, image_smoothing_enabled

Events

Mouse and keyboard events are forwarded to the kernel:

EventData
mouse_move, mouse_down, mouse_up, mouse_out{ x, y } in canvas coordinates
mouse_wheel{ x: deltaX, y: deltaY }
key_down{ key, shift_key, ctrl_key, meta_key }
canvas = Canvas(width=400, height=300)

def on_mouse_down(x, y):
    canvas.fill_circle(x, y, 5)

canvas.on_mouse_down(on_mouse_down)

Architecture

ipycanvas uses two widget models:

  • CanvasManagerModel — Headless singleton (_view_name: null) that receives ALL drawing commands from Python as binary custom messages. Uses switchCanvas to indicate which canvas each command targets.
  • CanvasModel — Visual widget that renders an HTML <canvas> element. Subscribes to its own comm_id and executes drawing commands on the 2D context.

Commands are serialized as binary buffers for performance. The first buffer contains JSON-encoded command metadata, and subsequent buffers carry binary data (e.g., NumPy arrays for batch operations).

The hold_canvas() context manager in Python batches many drawing commands into a single message for better performance. Without it, each drawing call is a separate message.

Implementation Notes

Our implementation differs from ipycanvas's original frontend in how command routing works. The binary protocol and command set are identical.

Store-level routing

In ipycanvas's original Backbone.js frontend, each CanvasView subscribes to the CanvasManagerModel's comm channel and tracks switchCanvas state locally. This means every canvas sees every command for every other canvas — they just filter locally.

We route at the store level instead. createCanvasManagerRouter (in canvas-manager-subscriptions.ts) watches for CanvasManagerModel instances, subscribes to their messages, parses switchCanvas targets, and re-emits each message to only the targeted canvas's comm_id. Each CanvasWidget subscribes to its own comm_id and processes everything it receives — no filtering, no shared state.

Original ipycanvas:
  Manager → broadcast to all canvases → each canvas filters locally

nteract-elements:
  Manager → store router parses switchCanvas → emits to target canvas only

This matters for correctness: with the broadcast approach, rapid animations on one canvas can interfere with other canvases' switchCanvas tracking. Store-level routing eliminates this by isolating each canvas completely.

Headless manager routing

CanvasManagerModel has _view_name: null — it's never in the widget render tree and has no visual representation. The original frontend handles this inside CanvasView by reaching into the manager's model. We handle it at the store level via createCanvasManagerRouter, which follows the same pattern as createLinkManager for jslink/jsdlink. This runs in WidgetStoreProvider and requires no component to be mounted.

No Backbone.js

ipycanvas's original frontend uses Backbone.js models and views. We use a pure React widget store with useSyncExternalStore for state and store-level subscriptions for custom messages.

Not Yet Supported

These features require cross-widget model resolution and are planned for a future release:

  • draw_image / put_image_data (drawing from other widget images)
  • stroke_path / fill_path (Path2D widget references)
  • sync_image_data (sending canvas pixels back to the kernel)
  • MultiCanvas (layered canvas composition)
  • RoughCanvas (hand-drawn style rendering)
  • Touch events

On this page