Vega renderer

vega-webgpu

Draws a Vega scenegraph on the GPU. Register it, pass renderer: 'webgpu', and the rest of the spec is unchanged.

Live demo Mark playground Render gallery GitHub npm

What it is

Vega ships a canvas renderer and an svg renderer. This is a third one, built on WebGPU. It registers itself with Vega's renderer module, so nothing about a spec changes: the same scenegraph is drawn with GPU pipelines instead of 2D canvas calls.

The canvas renderer is the reference. Every spec in the corpus is rendered both ways and compared pixel by pixel on every commit, so a difference is a tracked defect rather than a surprise. The render gallery shows every case as each of the last ten runs on main and every release drew it, and what is still off is listed under features and roadmap.

Needs a browser with WebGPU. Without one, 'webgpu' draws with the canvas renderer, see falling back.

Use it

Script tag

The renderer registers itself when loaded after Vega.

<script src="https://cdn.jsdelivr.net/npm/vega@6/build/vega.min.js"></script>
<script src="https://vega.github.io/vega-webgpu/releases/2_0_4/vega-webgpu-renderer.js"></script>
<div id="vis"></div>
<script>
  fetch('https://vega.github.io/vega/examples/bar-chart.vg.json')
    .then(res => res.json())
    .then(spec => {
      new vega.View(vega.parse(spec), {
        renderer: 'webgpu',
        container: '#vis',
        hover: true,
      }).runAsync();
    });
</script>

Every hosted build is listed under versions, each with its own page.

Match the Vega major version. 2.0.0 and later target Vega 6. Everything before it targets Vega 5 and throws Class constructor cannot be invoked without 'new' when loaded next to Vega 6, because a renderer registers itself against whichever Vega is already on the page.

Renderer Load this Vega
2.0.0 and later https://cdn.jsdelivr.net/npm/vega@6/build/vega.min.js
1.2.0 and earlier https://cdn.jsdelivr.net/npm/vega@5/build/vega.min.js

Falling back

Without navigator.gpu, 'webgpu' draws with the canvas renderer and warns. A browser can also have navigator.gpu and no adapter, a blocklisted GPU for instance, and then nothing is drawn. To fall back there too, pick the renderer from the adapter:

const renderer = (await navigator.gpu?.requestAdapter()) ? 'webgpu' : 'canvas';
new vega.View(vega.parse(spec), { renderer, container: '#vis' }).runAsync();

Config

Options live on view._renderer.wgOptions and can be changed between frames.

const view = new vega.View(vega.parse(spec), { renderer: 'webgpu', container: '#vis' });
view._renderer.wgOptions.debugLog = true;
Option What it does Default Since
sampleCount MSAA samples per pixel: 4 for antialiased, 1 for plain. Anything else falls back to 4. Pipelines rebuild on the next render. 4 2.0.0
redrawOnZoom Follow browser zoom. Zoom changes devicePixelRatio, and the default resizes the canvas and redraws so the view stays sharp. When false the canvas keeps the ratio it was first sized at and the browser scales it, which is softer but skips the redraw and cannot grow past the texture cap. true 2.0.0
renderLock Skip re-entrant render calls while a frame is in flight. The most recent request always runs, so an interactive chart stays responsive under a drag. true 1.1.1
cacheShapes Keep triangulated shape geometry between frames instead of rebuilding it. Costs memory on a scene whose geometry changes every frame. true 1.1.0
debugLog Log per-frame render timings to the console. false 1.0.0
offscreen Render into a texture the renderer owns and never touch the canvas swapchain. For a headless runner with no compositor, where acquiring the swapchain destroys the device. The canvas stays blank, so the frame is only reachable through captureFrame(). false 2.0.0
canvasTextDrift Place each label where the canvas renderer would, which can be a row off on a half pixel baseline. Only for matching a canvas view side by side. false 2.0.0

renderBatch and simpleLine were removed in 2.0.0. The line mark batches its segments unconditionally now, so neither had anything left to switch.

Feature completeness

All twelve of Vega's mark types are drawn: arc, area, group, image, line, path, rect, rule, shape, symbol, text and trail. Fill, stroke, opacity, clipping, corner radii, symbol shapes and rotation, and text layout all work on every mark that has them. The table is the properties that used to stop at some marks, and each now reaches every mark that has it.

Mark

Every mark name is a link into the playground, where you can tick these properties on and off and watch canvas and WebGPU draw the same spec side by side.

Known differences from canvas

Roadmap

Broad strokes. The full list, with the measurement behind each item, is in ToDo.md.

2.1.0 and later

Worth doing, not worth holding a release for.

  • Analytic edge coverage for triangulated fills, so an edge on a pixel boundary matches canvas rather than landing a quarter step off
  • Publish to npm
  • Viewport clipping, so a very tall view draws only what is on screen
  • Tiled rendering for a canvas past the GPU's texture cap
  • Per-item compositing, which is what would reproduce canvas's polygon seams
  • Performance: feed d3-geo a path sink, reuse buffers, cache bind groups

Set up locally

git clone https://github.com/vega/vega-webgpu.git
cd vega-webgpu
npm install
npm run build     # UMD, minified and ESM bundles plus type declarations, into build/
npm run serve     # serve the repo at http://localhost:5500

Then open http://localhost:5500/test/?spec=bar&renderer=webgpu&version=dev to run the local build against any spec in test/specs-valid. The page can show canvas and WebGPU side by side and a pixel diff between them.

Command What it does
npm run dev Rollup in watch mode
npm test Render every spec both ways and compare, from the build
npm run test:report Open the last run's HTML report
npm run gallery:record Record every case to test/render/output
npm run gallery Browse the recorded cases, or render them live
npm run typecheck Strict TypeScript, no emit
npm run lint eslint

Versions

Each build is hosted here and can be loaded straight from a script tag.

Version Changes Files
2.0.4 A packaging patch for 2.0.3: nothing the package installs is commonjs any more. The renderer is the same as 2.0.3. js min
2.0.3 The first release published to npm from the release workflow, with provenance. The renderer is the same as 2.0.2. js min
2.0.2 A patch for 2.0.1: a view with no size draws nothing, where it logged WebGPU validation errors on every frame. js min
2.0.1 A patch for 2.0.0: a stroked rect no longer draws its fill under the outer half of its stroke. js min
2.0.0 Built for Vega 6. Every mark type is drawn, most edges are antialiased analytically rather than by MSAA, and text goes through a glyph atlas. js min
1.2.0 Performance work on lines, clipping support and a path offset fix. (needs Vega 5) js min
1.1.1 Performance work on paths, plus the renderLock option. (needs Vega 5) js min
1.1.0 Overall improvements to performance, types and structure. (needs Vega 5) js min
1.0.0 First WebGPU implementation. (needs Vega 5) js min