Canvas

A drawing surface for freeform 2D rendering using Core Graphics. Canvas accepts a list of declarative drawing commands (shapes, images, text) and renders them with GPU acceleration on macOS.

The canvas is reactive -- calling draw(), append(), or clear() triggers an immediate re-render. Gesture callbacks enable interactive drawing applications such as freehand sketching or annotation tools.

For the full list of drawing commands, see the Draw Module Reference.

Constructor

nib.Canvas(
    width=100,
    height=100,
    commands=None,
    background_color=None,
    enable_gestures=False,
    on_pan_start=None,
    on_pan_update=None,
    on_pan_end=None,
    on_hover=None,
    **modifiers,
)

Parameters

Parameter Type Default Description
width float 100 Width of the canvas drawing area in points.
height float 100 Height of the canvas drawing area in points.
commands list[DrawCommand] None Initial list of drawing commands to render.
background_color str None Background color as a hex string (e.g., "#1a1a1a"). None for transparent.
enable_gestures bool False Enable pan/hover gesture tracking. Automatically enabled when any gesture callback is set.
on_pan_start Callable[[PanEvent], None] None Callback invoked when mouse/pen is pressed down on the canvas.
on_pan_update Callable[[PanEvent], None] None Callback invoked when mouse/pen is dragged across the canvas.
on_pan_end Callable[[PanEvent], None] None Callback invoked when mouse/pen is released.
on_hover Callable[[PanEvent], None] None Callback invoked when the mouse moves over the canvas without pressing.
**modifiers Common view modifiers: padding, opacity, corner_radius, etc.

Mutable Properties

Property Type Description
canvas_width float Get or set the drawing area width. Setting triggers a re-render.
canvas_height float Get or set the drawing area height. Setting triggers a re-render.
background_color str \| None Get or set the background color.
commands list[DrawCommand] Get the current list of drawing commands (read-only).
on_pan_start Callable \| None Get or set the pan start callback. Setting a callback auto-enables gestures.
on_pan_update Callable \| None Get or set the pan update callback.
on_pan_end Callable \| None Get or set the pan end callback.
on_hover Callable \| None Get or set the hover callback.

Methods

Method Signature Description
draw draw(commands: list[DrawCommand]) Replace all drawing commands and re-render.
append append(command: DrawCommand) Add a single drawing command and re-render.
clear clear() Remove all drawing commands and re-render.

PanEvent

A dataclass passed to gesture callbacks containing the pointer position.

Field Type Description
x float X coordinate of the event in canvas coordinates.
y float Y coordinate of the event in canvas coordinates.

Examples

Basic drawing

import nib

def main(app: nib.App):
    canvas = nib.Canvas(width=400, height=300, background_color="#1a1a1a")
    canvas.draw([
        nib.draw.Rect(x=10, y=10, width=100, height=50, fill="#3498db"),
        nib.draw.Circle(cx=200, cy=100, radius=40, fill="#e74c3c"),
        nib.draw.Text("Hello Canvas!", x=10, y=280, fill="#ffffff"),
    ])

    app.build(canvas)

nib.run(main)

Freehand drawing with gestures

import nib

def main(app: nib.App):
    canvas = nib.Canvas(width=400, height=300, background_color="#ffffff")
    last_pos = None

    def on_pan_start(e):
        nonlocal last_pos
        last_pos = (e.x, e.y)

    def on_pan_update(e):
        nonlocal last_pos
        canvas.append(nib.draw.Line(
            x1=last_pos[0], y1=last_pos[1],
            x2=e.x, y2=e.y,
            stroke="#000000", stroke_width=3,
        ))
        last_pos = (e.x, e.y)

    canvas.on_pan_start = on_pan_start
    canvas.on_pan_update = on_pan_update

    app.build(
        nib.VStack(controls=[
            canvas,
            nib.Button("Clear", action=canvas.clear),
        ], spacing=8, padding=16)
    )

nib.run(main)

Drawing with gradients

import nib

def main(app: nib.App):
    canvas = nib.Canvas(width=400, height=300)
    canvas.draw([
        nib.draw.Rect(
            x=10, y=10, width=180, height=130,
            fill=nib.draw.LinearGradient(
                start=(10, 10), end=(190, 140),
                colors=["#FF0000", "#0000FF"],
            ),
            corner_radius=8,
        ),
        nib.draw.Circle(
            cx=300, cy=100, radius=60,
            fill=nib.draw.RadialGradient(
                center=(300, 100), radius=60,
                colors=["#FFFF00", "#FF0000"],
            ),
        ),
    ])

    app.build(canvas)

nib.run(main)

Interactive dot painting

import nib

def main(app: nib.App):
    canvas = nib.Canvas(width=400, height=300, background_color="#f5f5f5")

    def on_pan_update(e):
        canvas.append(
            nib.draw.Circle(cx=e.x, cy=e.y, radius=5, fill="#3B82F6")
        )

    canvas.on_pan_update = on_pan_update
    app.build(canvas)

nib.run(main)