Canvas2D compatibility
Run Canvas2D-shaped drawing code on Valo and understand the known differences.
The valo-web entry point provides a Canvas2D-compatible recorder over the
same renderer as the engine API.
import { createValoCanvas } from 'valo-web';
const context = await createValoCanvas(document.querySelector('canvas')!);
context.fillStyle = 'rgba(200, 255, 61, 0.9)';
context.beginPath();
context.roundRect(24, 24, 200, 120, [34, 10, 34, 10]);
context.fill();Use this entry point when migrating Canvas2D-shaped code. For a new application that wants Valo-specific features, prefer the JavaScript engine API.
What is covered
The adapter implements the commonly used Canvas2D surface:
- Drawing state, save/restore, transforms and compositing
- Paths, fills, strokes, clips, gradients and patterns
- Images, image smoothing and pixel uploads
- Shadows and CSS filter chains
- Text drawing, font shorthand parsing and
TextMetrics Path2D, hit testing and transform queries
The conformance suite renders scenes through both Valo and Chrome's Canvas2D, then compares their output. It covers normal drawing as well as invalid, degenerate and generated command sequences.
Known differences
getImageData is not implemented because it requires a synchronous GPU
read-back. putImageData is supported.
- Patterns remain connected to their source instead of taking the browser's creation-time snapshot.
putImageDatarespects the active clip; browser Canvas2D ignores it.- The hanging text baseline uses a font-metric fallback rather than the
OpenType
BASEtable. isContextLost()does not yet observe WebGPU device loss.
These are compatibility limits, not behaviour the engine API inherits.
Valo extensions
The adapter exposes a few operations with no standard Canvas2D spelling:
context.saveLayer(alpha, blendMode);
context.setColorMatrix(matrix);
context.backdropBlur(x, y, width, height, sigma);For difference clips, gradient spread modes, mask blur styles and retained
display lists, use valo-web/raw.
Frame control
By default, changes are batched and presented on the next animation frame. Use manual presentation when your application already owns the frame loop:
const context = await createValoCanvas(canvas, { autoPresent: false });
context.beginFrame('#0e0e12');
draw(context);
const stats = context.present();Run npm run test:conformance in the repository to execute the browser
comparison suite.