canvaskit-wasm 0.39 build 2026-04-29

Canvas

A Canvas records draw operations against a backing surface. You don't create one directly — surface.getCanvas() hands you the canvas tied to that surface, and every draw* you call paints into the surface's pixels. Calling surface.flush() pushes the recorded operations to the GPU (or onto a 2D context for software-backed surfaces).

Canvas is not a WASM object you delete(); its lifetime is tied to its parent Surface. When the surface goes away, the canvas does too.

The state model is a stack: save() pushes the current transform/clip/layer state, restore() pops it. saveLayer() additionally pushes an offscreen buffer that gets composited back when popped — that's how blur backdrops, blend-mode-scoped draws, and opacity groups work.

A first draw

The simplest possible canvas program: clear, set up a paint, draw a shape, flush.

const paint = new CK.Paint(); paint.setColor(CK.Color(40, 90, 180, 1)); paint.setAntiAlias(true); canvas.clear(CK.WHITE); canvas.drawCircle(210, 128, 80, paint); surface.flush(); paint.delete();

Save / restore

save() pushes the current transform + clip + layer state. restore() pops it. They nest. Use them whenever a transform or clip should affect a region of your draws and not leak outward.

const paint = new CK.Paint(); paint.setColor(CK.Color(40, 90, 180, 1)); paint.setAntiAlias(true); canvas.clear(CK.WHITE); canvas.drawCircle(80, 128, 40, paint); canvas.save(); canvas.translate(180, 0); canvas.rotate(15, 0, 128); canvas.drawCircle(80, 128, 40, paint); canvas.restore(); canvas.drawCircle(340, 128, 40, paint); surface.flush(); paint.delete();

The middle circle inherits the rotated frame; the outer two stay axis-aligned because restore() brought the original transform back.

saveLayer with a backdrop filter

saveLayer(paint?, bounds?, backdrop?, flags?) pushes an offscreen buffer. When restore() runs, the buffer composites back into the parent layer through the layer's paint (which can carry blend modes, color filters, image filters). The optional backdrop filter samples the parent layer's pixels through that filter as the offscreen buffer is initialized — that's how frosted-glass blur works.

Error: Line 1: Unexpected identifier

Clipping

clipRect, clipRRect, clipPath intersect (or replace) the current clip region. Anything you draw afterward is masked to that region until the next restore().

const paint = new CK.Paint(); paint.setColor(CK.Color(220, 60, 60, 1)); paint.setAntiAlias(true); const rrect = CK.RRectXY(CK.LTRBRect(120, 50, 300, 200), 30, 30); loop(() => { canvas.clear(CK.WHITE); canvas.save(); canvas.clipRRect(rrect, CK.ClipOp.Intersect, true); for (let i = 0; i < 12; i++) { canvas.save(); canvas.translate(mouse.x || 210, mouse.y || 130); canvas.rotate(i * 30, 0, 0); canvas.drawRect(CK.LTRBRect(-200, -6, 200, 6), paint); canvas.restore(); } canvas.restore(); surface.flush(); });

Transformations

translate, scale, rotate, skew, concat left-multiply onto the current matrix. Reading: each call is "apply this transform to whatever was there before". The matrix is Matrix3x3 in 2D.

const paint = new CK.Paint(); paint.setStyle(CK.PaintStyle.Stroke); paint.setStrokeWidth(2); paint.setColor(CK.Color(40, 90, 180, 1)); paint.setAntiAlias(true); const tile = CK.LTRBRect(-30, -30, 30, 30); loop(() => { canvas.clear(CK.WHITE); for (let i = 0; i < 8; i++) { canvas.save(); canvas.translate(60 + i * 45, 130); canvas.scale(1 - i * 0.06, 1 - i * 0.06); canvas.rotate(i * 12 + (mouse.x || 0) * 0.2, 0, 0); canvas.drawRect(tile, paint); canvas.restore(); } surface.flush(); });

Common methods

MemberArgsReturnsNotes
clearcolor: ColorvoidFill the entire canvas with a single color.
clipPathpath: Path, op: ClipOp, antiAlias: booleanvoidIntersect or replace the clip region with a path.
clipRectrect: Rect, op: ClipOp, antiAlias: booleanvoidClip to a rectangle.
clipRRectrrect: RRect, op: ClipOp, antiAlias: booleanvoidClip to a rounded rectangle.
concatmatrix: MatrixvoidLeft-multiply the given matrix onto the current transform.
drawArcoval: Rect, startAngle, sweepAngle: number, useCenter: boolean, paint: PaintvoidDraw an arc bounded by oval.
drawAtlasatlas: Image, srcRects, dstXforms, colors?, blendMode?: BlendMode, paint?: PaintvoidBatch-draw sprites from a single atlas image.
drawCirclecx, cy, r: number, paint: PaintvoidFill or stroke a circle.
drawColorcolor: Color, blendMode?: BlendModevoidFlood-fill the canvas with a color, blended.
drawImageimg: Image, x, y: number, paint?: PaintvoidDraw an image at (x, y) at its native size.
drawImageRectimg: Image, src: Rect, dst: Rect, paint: Paint | nullvoidDraw a sub-rect of an image scaled into a destination rect. Used for sprite atlases.
drawLinex0, y0, x1, y1: number, paint: PaintvoidStroke a line segment.
drawOvalrect: Rect, paint: PaintvoidFill or stroke an oval inscribed in rect.
drawDRRectouter: RRect, inner: RRect, paint: PaintvoidFill the area between outer and inner — donut shape with rounded corners.
drawPaintpaint: PaintvoidFill the entire canvas with the paint (color, shader, image filter).
drawPatchcubics: Float32Array | number[24], colors?: Color[4] | null, textureCoords?: Float32Array | number[8] | null, blendMode: BlendMode, paint: PaintvoidCoons-patch — a 4-side bicubic mesh. cubics is 12 control points (24 floats). Per-corner colors blend bilinearly across the patch.
drawPathpath: Path, paint: PaintvoidFill or stroke a path.
drawPicturepicture: PicturevoidReplay a recorded picture into this canvas.
drawPointsmode: PointMode, points: Float32Array | number[], paint: PaintvoidDraw a flat [x0, y0, x1, y1, …] array as points / lines / a polyline depending on mode. Stroke cap controls the dot shape.
drawRectrect: Rect, paint: PaintvoidFill or stroke a rectangle.
drawRRectrrect: RRect, paint: PaintvoidFill or stroke a rounded rectangle.
drawShadowpath: Path, zPlaneParams, lightPos, lightRadius: number, ambientColor, spotColor: Color, flags: numbervoidDraw a soft shadow under path as if lit from lightPos.
drawVerticesvertices: Vertices, blendMode: BlendMode, paint: PaintvoidDraw a triangle mesh with optional per-vertex colors and texture coords.
getDeviceClipBoundsIRectBounds of the current clip in device pixels.
getLocalToDeviceMatrixThe 4x4 matrix from local space to device space.
getTotalMatrixMatrixThe current 3x3 transform matrix.
readPixelsx, y: number, info: ImageInfo, dst?: MallocObj, bytesPerRow?: numberUint8Array | Float32Array | nullRead pixels from the canvas back to JS.
restorevoidPop one save / saveLayer level.
rotatedegrees, cx, cy: numbervoidRotate the current transform around (cx, cy).
savenumberPush current transform + clip. Returns the save count.
saveLayerpaint?: Paint, bounds?: Rect, backdrop?: ImageFilter, flags?: numbernumberPush an offscreen layer. Composited via paint on restore. backdrop filters the underlying layer.
scalesx, sy: numbervoidScale the current transform.
skewsx, sy: numbervoidSkew the current transform.
translatedx, dy: numbervoidTranslate the current transform.
writePixelspixels: Uint8Array | number[], srcWidth, srcHeight, x, y: numberbooleanBlit a pixel buffer onto the canvas at (x, y).

Runtime extensions (this site only)

Canvas instances on the doc-page bundle carry two extra helpers, defined client-side:

MemberArgsReturnsNotes
drawAnchorspath: PathvoidDraws a small dot at every move/line/quad/cubic endpoint.
drawTangentspath: PathvoidDraws each cubic's two tangent handles in red, with dots on anchors and controls.

These are not part of CanvasKit — they're implemented in the doc runtime so examples can visualize the curves without each demo redefining the helper.

See also

  • Surface — owns the canvas; flush() pushes pixels.
  • Paint — color, style, blend, filters consumed by drawX calls.
  • Path — geometry passed to drawPath / clipPath.
  • BlendMode — composite ops for drawColor / via Paint.setBlendMode.
  • ClipOp — Intersect or Replace.
  • Memory management — Canvas is not deleted directly; the surface owns it.