---
url: /mosaic/vgplot.md
---

**(begin JavaScript API)**

A grammar of interactive graphics in which graphical marks are Mosaic clients.

As the name suggests, `vgplot` combines concepts from existing tools such as Vega-Lite, ggplot2, and Observable Plot.
Like Vega-Lite, vgplot supports rich interactions and declarative specification either using an API or standalone JSON/YAML specs (via the [`mosaic-spec`](/spec/) package).
However, because vgplot is based on Mosaic, it interoperates with other Mosaic clients, such as the included [Mosaic Inputs](/inputs/).
vgplot calls Observable Plot to render SVG output.

This page provides an overview of vgplot.
Skip to the [examples](/examples/) to dive right in.

::: tip
`vgplot` re-exports much of the `mosaic-core`, `mosaic-sql`, `mosaic-plot`, and `mosaic-inputs` packages. For many applications, it is sufficient to import `@uwdata/vgplot` alone.
:::

## Plots

A `plot` produces a single visualization as a Web element.
A `plot` is defined as a list of directives defining plot [attributes](#attributes), [marks](#marks), [interactors](#interactors), or [legends](#legends).

Similar to other grammars, a `plot` consists of *marks*—graphical primitives such as bars, areas, and lines—which serve as chart layers.
We use the semantics of Observable Plot, such that each `plot` has a dedicated set of encoding *channels* with named *scale* mappings such as `x`, `y`, `color`, `opacity`, etc.
Plots support faceting of the `x` and `y` dimensions, producing associated `fx` and `fy` scales.
Plots are rendered to SVG output by marshalling a specification and passing it to Observable Plot.

::: code-group

```js [JavaScript]
import { plot, lineY, from, width, height } from "@uwdata/vgplot";
plot(
  lineY(from("aapl"), { x: "Date", y: "Close" }),
  width(680),
  height(200)
)
```

```yaml [YAML]
plot:
- mark: lineY
  data: { from: aapl }
  x: Date
  y: Close
width: 680
height: 200
```

:::

The stock chart above consists of three directives:

1. A `lineY` mark to visualize data from a backing data table named `"aapl"`.
2. A `width` attribute to set the chart width in pixels.
3. A `height` attribute to set the chart height in pixels.

[Plot API Reference](/api/vgplot/plot)

## Attributes

*Attributes* are plot-level settings such as `width`, `height`, margins, and scale options (e.g., `xDomain`, `colorRange`, `yTickFormat`). Attributes may be [`Param`](/core/#params)-valued, in which case a plot updates upon param changes.

vgplot includes a special `Fixed` scale domain setting (e.g., `xDomain(Fixed)`), which instructs a plot to first calculate a scale domain in a data-driven manner, but then keep that domain fixed across subsequent updates.
Fixed domains enable stable configurations without requiring a hard-wired domain to be known in advance, preventing disorienting scale domain "jumps" that hamper comparison across filter interactions.

[Attributes API Reference](/api/vgplot/attributes)

## Marks

*Marks* are graphical primitives, often with accompanying data transforms, that serve as chart layers.
In vgplot, each mark is a Mosaic client that produces queries for needed data.
Marks accept a data source definition and a set of supported options, including encoding *channels* (such as `x`, `y`, `fill`, and `stroke`) that can encode data *fields*.

A data field may be a column reference or query expression, including dynamic param values.
Common expressions include aggregates (`count`, `sum`, `avg`, `median`, *etc.*), window functions (such as [moving averages](/examples/moving-average)), date functions, and a `bin` transform.
Most field expressions—including aggregate, window, and date functions—are specified using [Mosaic SQL](/sql/) builder methods.

Marks support dual modes of operation: if an explicit array of data values is provided instead of a backing `from(tableName)` reference, vgplot will visualize that data without issuing any queries to the database. This functionality is particularly useful for adding manual annotations, such as custom rules or text labels.

[Marks API Reference](/api/vgplot/marks)

::: warning
Interactive filtering is not supported if you bypass the database and pass data directly to a mark.
:::

### Basic Marks

Basic marks, such as `dot`, `bar`, `rect`, `cell`, `text`, `tick`, and `rule`, mirror their namesakes in [Observable Plot](https://observablehq.com/plot/).
Variants such as `barX` and `rectY` indicate spatial orientation and data type assumptions. `barY` indicates vertical bars—continuous `y` over an ordinal `x` domain—whereas `rectY` indicates a continuous `x` domain.

Basic marks follow a straightforward query construction process:

* Iterate over all encoding channels to build a `SELECT` query.
* If no aggregates are encountered, query all fields directly.
* If aggregates are present, include non-aggregate fields as `GROUP BY` criteria.
* If provided, map filtering criteria to a SQL `WHERE` clause.

### Connected Marks

The `area` and `line` marks connect consecutive sample points.
Connected marks are treated similarly to basic marks, with one notable addition: the queries for spatially oriented marks (`areaY`, `lineX`) can apply [M4 optimization](https://observablehq.com/@uwdata/m4-scalable-time-series-visualization). The query construction method uses plot width and data min/max information to determine the pixel resolution of the mark range. When the data points outnumber available pixels, M4 performs perceptually faithful pixel-aware binning of the series, limiting the number of drawn points. This optimization offers dramatic data reductions for both single and multiple series.

Separately, vgplot includes a `regressionY` mark for linear regression fits. Regression calculations and associated statistics are performed in-database in a single aggregate query. The mark then draws the regression line and optional confidence interval area.

### Geography and Geometry

The `geo` mark visualizes geometry data, such as geographic regions, in GeoJSON format.
An array of GeoJSON features can be provided directly as data, or geographic data can be loaded and queried directly in database using the DuckDB `spatial` extension.

### Density Marks

The `densityY` mark performs 1D kernel density estimation (KDE).
The `densityY` mark defaults to areas, but supports a `type` option to instead use lines, points, or other basic marks.
The generated query performs *linear binning*, an alternative to standard binning that proportionally distributes the weight of a point between adjacent bins to provide greater accuracy for density estimation. The query uses subqueries for the "left" and "right" bins, then aggregates the results. The query result is a 1D grid of binned values which are then smoothed. As smoothing is performed in the browser, interactive bandwidth updates are processed immediately.

The `density`, `contour`, `heatmap`, and `raster` marks compute densities over a 2D domain using either linear (default) or standard binning. Smoothing again is performed in browser; setting the `bandwidth` option to zero disables smoothing. The `contour` mark then performs contour generation, whereas the `raster` mark generates a colored bitmap. The `heatmap` mark is a convenient shortcut for a `raster` that performs smoothing by default. Dynamic changes of bandwidth, contour thresholds, and color scales are handled immediately in browser.

The `hexbin` mark pushes hexagonal binning and aggregation to the database. Color and size channels may be mapped to `count` or other aggregates. Hexagon plotting symbols can be replaced by other basic marks (such as `text`) via the `type` option.

The `denseLine` mark creates a density map of line segments, rather than points.
Line density estimation is pushed to the database. To ensure that steep lines are not over-represented, we approximate arc-length normalization for each segment by normalizing by the number of filled raster cells on a per-column basis. We then aggregate the resulting weights for all series to produce the line densities.

## Interactors

*Interactors* imbue plots with interactive behavior. Most interactors listen to input events from rendered plot SVG elements to update bound [*selections*](/core/#selections). Interactors take facets into account to properly handle input events across subplots.

The `toggle` interactor selects individual points (e.g., by click or shift-click) and generates a selection clause over specified fields of those points. Directives such as `toggleColor`, `toggleX`, and `toggleY` simplify specification of which channel fields are included in the resulting predicates.

The `nearestX` and `nearestY` interactors select the nearest value along the `x` or `y` encoding channel.

The `intervalX` and `intervalY` interactors create 1D interval brushes.
The `intervalXY` interactor creates a 2D brush.
Interval interactors accept a `pixelSize` parameter that sets the brush resolution: values may snap to a grid whose bins are larger than screen pixels and this can be leveraged to optimize query latency.

The `panZoom` interactor produces interval selections over corresponding `x` or `y` scale domains. Setting these selections to a plot's `xDomain` and/or `yDomain` attributes will cause the plot to pan and zoom in response.

The `highlight` interactor updates the rendered state of a visualization in response to a Selection.
Non-selected points are set to translucent, neutral gray, or other specified visual properties.
Selected points maintain normal encodings.
We perform highlighting by querying the database for a selection bit vector and then modifying the rendered SVG.

[Interactors API Reference](/api/vgplot/interactors)

## Legends

*Legends* can be added to `plot` specifications or included as standalone elements.

The `name` directive gives a `plot` a unique name.
A standalone legend can reference a named plot (`colorLegend({ for: 'name' })`) to avoid respecifying scale domains and ranges.

Legends also act as interactors, taking a bound Selection as a parameter.
For example, discrete legends use the logic of the `toggle` interactor to enable point selections.
Two-way binding is supported for Selections using *single* resolution, enabling legends and other interactors to share state.

[Legends API Reference](/api/vgplot/legends)

## Layout

Layout helpers combine elements such as [plots](#plots) and [inputs](/inputs/) into multi-view dashboard displays.
vgplot includes `vconcat` (vertical concatenation) and `hconcat` (horizontal concatenation) methods for multi-view layout.
These methods accept a list of elements and position them using CSS `flexbox` layout.
Layout helpers can be used with plots, inputs, and arbitrary Web content such as images and videos.
To ensure spacing, the `vspace` and `hspace` helpers add padding between elements in a layout.

[Layout API Reference](/api/vgplot/layout)


**(end JavaScript API)**

**(begin Python API)**

A grammar of interactive graphics in which graphical marks are Mosaic clients.

In Python, `import vgplot as vg` gives you composable helpers for plots, attributes, marks, interactors, legends, and layout. Names use **`snake_case`**. Key Python-specific conventions: use `vg.source("table")` (not `from_`) to reference a named database table; interactors take `bind=` (not `as=`) for their selection; legends take `plot=` (not `for`) to reference a named plot.

The interactive figure above is driven by the same [declarative specification](/spec/) used across Mosaic (YAML in the docs site). In notebooks you usually pass an equivalent structure as a dict—built with `vg.*` helpers, loaded from YAML/JSON, or produced by your own tooling—to [`MosaicWidget`](/jupyter/) as `spec`.

More copy-paste examples live under [Examples](/examples/) (open the **Python** tab on each page).

::: tip
The fastest path in Jupyter is often YAML or JSON plus `MosaicWidget(spec=..., data=...)`. Use `vgplot` when you want to assemble or adjust specs in code. Option names match the [specification format](/api/spec/format); builder helpers line up with those names in snake\_case.
:::

## Plots

`vg.plot` takes a sequence of directives: [attributes](#attributes), [marks](#marks), [interactors](#interactors), and [legends](#legends).

Each plot uses [Observable Plot](https://observablehq.com/plot/)–style *channels* (`x`, `y`, `fill`, `opacity`, …) and optional faceting scales `fx` and `fy`. The widget renders charts in the browser as SVG.

```python
import vgplot as vg

vg.plot(
    vg.line_y(vg.source("aapl"), x="Date", y="Close"), vg.width(680), vg.height(200)
)
```

This chart uses three directives:

1. A `line_y` mark with `vg.source("aapl")`.
2. `vg.width(680)`.
3. `vg.height(200)`.

[Plot reference](/api/vgplot/plot)

## Attributes

*Attributes* set plot-level options: size, margins, and scales (`x_domain`, `color_range`, `y_tick_format`, …) via helpers such as `vg.x_domain(...)`, `vg.color_range(...)`, `vg.y_tick_format(...)`. Param references like `"$point"` tie encodings to widget state.

**`"Fixed"`** domains (e.g. `vg.x_domain("Fixed")`) compute an initial domain from data, then freeze it so filtered views do not rescale in a distracting way.

[Attributes reference](/api/vgplot/attributes)

## Marks

*Marks* are layers backed by Mosaic queries. They usually take a `vg.source("table")` reference (or a `DataDef` from `vg.parquet()`, `vg.csv()`, etc.) as the first argument, plus channel options. Fields may be columns, SQL fragments, or param strings.

You can pass static rows instead of a database source for annotations; that path skips the database and does not participate in linked filtering.

[Marks reference](/api/vgplot/marks)

::: warning
Interactive filtering requires data that flows through the coordinator (via `vg.source()` / registered tables), not ad hoc Python lists alone.
:::

### Basic marks

Primitives include `dot`, `bar`, `rect`, `cell`, `text`, `tick`, and `rule`, consistent with Observable Plot. Oriented variants appear as `bar_x`, `bar_y`, `rect_x`, `rect_y`, and so on—the YAML `mark` string (e.g. `barY`) corresponds to `vg.bar_y(...)`.

Query planning walks channels to build `SELECT`, adds `GROUP BY` when aggregates appear, and applies `WHERE` for active selections.

### Connected marks

`area` and `line` marks connect ordered samples (`vg.area_y`, `vg.line_y`, …). Large series can use M4-style pixel-aware downsampling so draw cost stays bounded.

`regression_y` computes fits and optional intervals in the database, then draws the line and band.

### Geography and geometry

`geo` draws GeoJSON geometry, either inlined or loaded through DuckDB (including the `spatial` extension).

### Density marks

`density_y` is 1D KDE. `density`, `contour`, `heatmap`, and `raster` cover 2D surfaces; `hexbin` aggregates hex cells in SQL; `dense_line` estimates density along polylines. Bandwidth and smoothing options follow the declarative spec.

## Interactors

*Interactors* connect pointer input to [*selections*](/core/#selections). Helpers include `toggle`, `toggle_color`, `toggle_x`, `toggle_y`, `nearest_x`, `nearest_y`, `interval_x`, `interval_y`, `interval_xy` (with `pixel_size` where applicable), `pan_zoom`, and `highlight`.

Wiring `pan_zoom` selections into `x_domain` / `y_domain` (or equivalent scale bindings) implements pan and zoom.

[Interactors reference](/api/vgplot/interactors)

## Legends

Legends attach inside `vg.plot` or as separate elements. Naming a plot lets another legend reuse its scales, e.g. `vg.color_legend(plot="my_plot")`.

Discrete color legends can drive the same toggle-style selection behavior as point interactors.

[Legends reference](/api/vgplot/legends)

## Layout

`vg.vconcat`, `vg.hconcat`, `vg.vspace`, and `vg.hspace` compose plots, [inputs](/inputs/), and spacing. The runtime lays out children with flexbox.

Full apps often call `vg.spec(meta=..., data=..., params=..., view=...)` so the result is a single top-level spec object (the same shape as JSON/YAML on disk).

[Layout reference](/api/vgplot/layout)

## Related documentation

* [Mosaic spec](/spec/) — portable JSON/YAML format shared by Python and the docs examples.
* [Specification format reference](/api/spec/format) — schema-oriented description of top-level keys and marks.
* [vgplot under **API Reference**](/api/) — detailed plot, mark, interactor, and layout pages. Option names there appear in **camelCase** (e.g. `lineY`, `xDomain`); in Python, use **snake\_case** (`line_y`, `x_domain`).

**(end Python API)**
