vellumwidget 0.5.0
-
textargument onas_widget(). Choose how text is written into the SVG,
passed through tovellum::scene_svg():"native"(the default) emits
selectable<text>referencing system fonts — smaller when the page has the
font, post-processable, and better for accessibility and LLMs — while
"outline"emits pixel-faithful, font-independent glyph paths. Applies to the
per-element SVG path only; in raster mode text is baked into the base image and
the argument is ignored (with a warning if set explicitly) (#1). -
New articles. Two articles document the features added since 0.4.0:
Linking views with crosstalk (coordinating a widget with DT / plotly /
leaflet and crosstalk'sfilter_*inputs) and Very large scenes (raster
mode, the spatial index, the columnar payload, and crisp zoom). -
Crisp zoom in raster mode. When you zoom into a raster-mode plot, the base
image used to upscale and blur. The widget now redraws the points sharply on a
<canvas>overlay while zoomed in — sampling each point's colour straight from
the rendered image and its position/size from the element index, so the crisp
layer matches what vellum drew. It engages only when zoomed in (the faithful,
anti-aliased base image still shows at the full view), redraws just the points in
view, and degrades gracefully to the image alone where a 2D canvas context isn't
available. Entirely client-side; no change to the payload or to small/moderate
(SVG-mode) plots. -
Very large scatterplots are navigable (raster mode).
as_widget()gains a
modeargument ("auto"/"svg"/"raster"). In"auto"(the default), a
scene with more thanraster_thresholdkeyed elements (default20000) is drawn
once as a single embedded image instead of one SVG node per element, and all
interaction — hover tooltips + highlight, click/brush select, pan/zoom — is driven
client-side from the element index (bounding boxes + keys) rather than the DOM.
A 150k-point keyed scatter that previously produced a ~75 MB SVG with 150,000 DOM
nodes now ships as a ~0.8 MB image plus a compact index, with a handful of DOM
nodes, and stays smooth to hover and pan. Small and moderate plots are unchanged
(they keep the per-element SVG). Trade-offs in raster mode: per-element grammar
colours, per-mark screen-reader focus, and display-tier cross-filtering don't
apply (there are no per-element nodes), and a zoomed-in view is a scaled raster
until re-rendered. Adds a dependency onbase64enc. -
Smoother hover, brush, and pan on large plots. Two client-side changes lift
the per-interaction cost that made big scatterplots laggy:- Spatial index. Nearest-mark hover and rectangular brush now hit-test
against a Flatbush R-tree
(O(log n) / O(k)) instead of scanning every element each time. The nearest-mark
scan runs on every pointer move, so this is the change you feel most. - Cheaper hover dim. Above a threshold, hovering dims the plot once (via the
holder's opacity) and re-draws the hovered marks crisply in a small overlay —
O(hovered) — instead of restyling every element via CSS (O(n)), which forced
a full-scene style recalc on each hover. Small and moderate plots keep the exact
previous per-mark dim. (Phase 2 of vellum's big-data interactivity plan.)
- Spatial index. Nearest-mark hover and rectangular brush now hit-test
-
Much faster, smaller payload for large plots (columnar element table). The
keyed-element metadataas_widget()embeds is now serialised in a columnar
form (one array per field) instead of one JSON object per element. This is a
transparent wire-format change — the widget behaves identically — but it removes
the per-element serialisation cost that dominated at large N. On a 150,000-point
keyed scatter the payload build + serialise dropped from ~24 s / 89 MB to
~0.4 s / 12 MB (~60x faster), so a big interactive scatter is no longer choked
by payload generation. Small and moderate plots are unaffected. (This is Phase 1
of vellum's big-data interactivity plan; the browser-side DOM/hover work for
truly huge scatters follows in later phases.) -
Error bars and boxplots are interactive. Now that vellumplot keys these
statistical marks, the widget hovers, tooltips, clicks/selects, and brushes them
as units: an error bar's bar + caps, or a box's rect + median + whiskers, all
light up and select together because they share onedata-key(outliers stay
individually addressable). Brush selection no longer double-counts a mark whose
key spans several SVG elements —input$<id>_brush$keysreports each such key
once. No runtime change was needed for the core behaviour; it already grouped
every node sharing a key.