ipycanvas Documentation
repository·master·Indexed 20 days ago
https://github.com/jupyter-widgets-contrib/ipycanvasAn interactive widgets library that provides a Python interface to the browser's Canvas API for Jupyter notebooks and JupyterLab. It enables high-performance drawing of primitives like text, lines, polygons, and images. Key features include the MultiCanvas for layered surfaces, the hold_canvas context manager for batched updates and smooth animations, and vectorized drawing methods using NumPy for optimized performance.
What's inside ipycanvas
- ipycanvas is a lightweight, fast, and stable library that exposes the browser's Canvas API to IPython. It allows developers to draw primitives—such as text, lines, polygons, arcs, and images—directly from Python code within a Jupyter environment. This enables the creation of interactive graphics, custom plotting libraries, and complex animations entirely in Python.
Manage canvas state with save() and restore()
masteripycanvasuses a stack-based system to manage drawing states. This allows you to temporarily change canvas attributes or transformations and then revert to a previous state without manually resetting every property.save(): Pushes the current drawing state onto the stack. The state includes all active transformations (translate, rotate, scale) and specific drawing attributes.restore(): Pops the most recently saved state from the stack and restores the canvas to that state.
Saved Attributes
A state saved via
save()includes the current values of:- Transformations: All applied translations, rotations, and scales.
- Styles:
stroke_style,fill_style,global_alpha,global_composite_operation. - Line Properties:
line_width,line_cap,line_join,miter_limit,line_dash_offset. - Text Properties:
font,text_align,text_baseline,direction.
You can nest these calls to create complex drawing workflows where different sections of a drawing use different settings.
# Example pattern for state management canvas.save() # Apply transformations and styles canvas.translate(10, 10) canvas.fill_style = 'red' canvas.fill_rect(0, 0, 50, 50) canvas.restore() # The canvas is now back to its original state (no translation, original fill_style) canvas.fill_rect(0, 0, 50, 50)Use styled vectorized methods for batch drawing
masterIpycanvas provides specialized 'styled' methods to draw multiple shapes simultaneously, where each shape can have its own unique color and alpha (transparency) value. This is highly efficient for rendering large numbers of objects using NumPy arrays.
Available styled methods include:
- Rectangles:
fill_styled_rects,stroke_styled_rects - Circles:
fill_styled_circles,stroke_styled_circles - Arcs:
fill_styled_arcs,stroke_styled_arcs - Polygons:
fill_styled_polygons,stroke_styled_polygons - Line Segments:
fill_styled_line_segments,stroke_styled_line_segments
import numpy as np from ipycanvas import Canvas, hold_canvas canvas = Canvas(width=400, height=300) n_rects = 300 x = np.random.randint(0, canvas.width, size=(n_rects)) y = np.random.randint(0, canvas.width, size=(n_rects)) width = np.random.randint(10, 40, size=(n_rects)) height = np.random.randint(10, 40, size=(n_rects)) colors_fill = np.random.randint(0, 255, size=(n_rects, 3)) colors_outline = np.random.randint(0, 255, size=(n_rects, 3)) alphas = np.random.random(n_rects) with hold_canvas(): canvas.fill_styled_rects(x, y, width, height, color=colors_fill, alpha=alphas) canvas.line_width = 2 canvas.stroke_styled_rects(x, y, width, height, color=colors_outline, alpha=alphas)- Rectangles:
Understanding the ipycanvas coordinate system and API style
masterThe
ipycanvaswidget exposes the Web Canvas API, but with two key differences for Python users:- Naming Convention: All API methods use
snake_caseinstead of the JavaScriptcamelCase. For example,canvas.fill_style = 'red'in Python replacescanvas.fillStyle = 'red'in JavaScript. - Coordinate System: The canvas grid origin
(0,0)is located at the top left corner. All coordinates are relative to this origin. An element at(x, y)isxpixels from the left andypixels from the top.
For more detailed information on the underlying API, refer to the MDN Canvas API documentation.
- Naming Convention: All API methods use
Understand the ipycanvas API differences from Web Canvas API
masterWhile
ipycanvasexposes the Web Canvas API, it follows Python conventions and includes specific helper methods. Key differences to note:- Naming Convention: All API methods and properties use
snake_caseinstead of the JavaScriptcamelCase. For example, usecanvas.fill_style = 'red'instead ofcanvas.fillStyle = 'red'. - Clear Shortcut: The
canvas.clear()method is a convenient shortcut forcanvas.clear_rect(0, 0, canvas.width, canvas.height). - Enhanced
put_image_data: Unlike the standard Web canvasputImageDatamethod,ipycanvas.Canvas.put_image_datasupports transparency and the current transformation state. - Performance Optimization: Use the
hold_canvascontext manager when performing a large number of drawing commands at once to improve performance.
- Naming Convention: All API methods and properties use
Enable image synchronization for Canvas retrieval
masterBy default,
ipycanvasdoes not synchronize the image state between the TypeScript front-end and the Python back-end to maintain high performance. To use methods liketo_file()orget_image_data(), you must explicitly enable synchronization by settingsync_image_data=Trueduring the initialization of yourCanvasorMultiCanvasobject.Once you have finished retrieving the image data, you can set
sync_image_databack toFalseto restore performance.from ipycanvas import Canvas # Must set sync_image_data=True to retrieve image data canvas = Canvas(width=200, height=200, sync_image_data=True)Draw shapes using Path commands
masterTo create custom shapes from scratch, follow this lifecycle:
- Call
begin_path()to start a new path. - Use drawing commands (like
line_to,arc, orbezier_curve_to) to build the path. - Call
stroke()to draw the outline orfill(rule)to fill the interior.
Fill Rules: When calling
fill(rule), you can specify:nonzero(default)evenodd
from ipycanvas import Canvas canvas = Canvas(width=100, height=100) # 1. Start the path canvas.begin_path() # 2. Add segments canvas.move_to(75, 50) canvas.line_to(100, 75) canvas.line_to(100, 25) # 3. Render canvas.fill() canvas- Call
Handle asynchronous image retrieval in a single Notebook cell
masterBecause drawing commands are processed asynchronously, you should use the
observemethod to wait for theimage_datatrait to update before attempting to save a file or process a NumPy array. This allows you to keep all setup, drawing, and retrieval logic within a single Notebook cell.Pattern for saving to file:
from ipycanvas import Canvas canvas = Canvas(width=200, height=200, sync_image_data=True) def save_to_file(*args, **kwargs): canvas.to_file("my_file.png") canvas.observe(save_to_file, "image_data") # Perform drawings here...Pattern for retrieving NumPy array:
from ipycanvas import Canvas canvas = Canvas(width=200, height=200, sync_image_data=True) def get_array(*args, **kwargs): arr = canvas.get_image_data() # Do something with arr canvas.observe(get_array, "image_data") # Perform drawings here...Create a Canvas or MultiCanvas
masterYou can create a single drawing surface using
Canvasor a multi-layered surface usingMultiCanvas.MultiCanvasis useful for separating static backgrounds from frequently updated foreground objects. Layers in aMultiCanvascan be accessed via index notation.from ipycanvas import Canvas # Create a single canvas canvas = Canvas(width=200, height=200)Watch and run ipycanvas during development
masterWhen developing the extension in JupyterLab, you can use
jlpm run watchin one terminal to monitor the source directory and automatically rebuild the extension when changes are detected. Use a second terminal to runjupyter lab.# Watch the source directory in one terminal, automatically rebuilding when needed jlpm run watch # Run JupyterLab in another terminal jupyter labCreate an animation loop using the 'slow' approach
masterFor most use cases, you can create an animation loop using a standard Python
fororwhileloop combined withtime.sleep().Key Requirements:
- Use the
hold_canvascontext manager inside the loop to wrap your drawing commands. This is essential for performance as it batches updates. - Call
canvas.clear()at the start of each iteration to remove the previous frame. - Use
time.sleep()to control the animation frequency (e.g.,sleep(0.02)for ~50Hz).
Limitations: This approach may suffer from latency if the Jupyter server and the client are on different machines (e.g., using MyBinder).
from time import sleep from ipycanvas import Canvas, hold_canvas canvas = Canvas() display(canvas) # Number of steps in your animation steps_number = 200 for i in range(steps_number): with hold_canvas(): # Clear the old animation step canvas.clear() # Perform all your drawings here # ... # Animation frequency ~50Hz = 1./50. seconds sleep(0.02)- Use the
Try ipycanvas online
masterYou can test
ipycanvaswithout local installation usingNotebook.link. This allows you to run examples directly in your browser.https://notebook.link/github/jupyter-widgets-contrib/ipycanvas/tree/main/lab/?path=examples%2F01.index.ipynb