Lasso Selection
interactionAlso known as: freeform select, lasso brush, polygon select, rubber band select
Description
Lasso selection is a freeform drawing interaction that lets users trace an arbitrary closed polygon around the data points they want to select. Unlike a rectangular brush, which constrains selection to axis-aligned regions, a lasso follows the cursor path exactly, allowing the user to capture irregularly shaped clusters, curved boundaries, or scattered groups of marks that share a conceptual relationship but not a simple rectangular footprint.
The technique originates in graphics editing software (Photoshop’s lasso tool dates to the early 1990s) and was adopted by visualization systems where data clusters are not axis-aligned. In a scatterplot with multiple overlapping groups, a rectangular brush inevitably includes unwanted points or excludes desired ones at cluster boundaries. A lasso solves this by giving the user full geometric control. Systems like Tableau, Vega-Lite (via selection parameters), and custom D3 implementations all support lasso selection.
From a cognitive standpoint, lasso selection maps closely to the user’s visual perception of group boundaries. When someone sees a cluster of points, they perceive a convex or concave hull — and the lasso lets them directly trace that perceived boundary. The cost is higher motor effort compared to a simple drag-to-brush, and the resulting polygon may not be perfectly reproducible. For this reason, lasso is typically offered alongside rectangular brush as an alternative selection mode, activated by a modifier key or toolbar toggle.
When to Use
- When data clusters are irregularly shaped and a rectangular brush would select too many or too few points.
- In scatterplots or geographic maps where the user needs to isolate non-convex regions.
- When the selection needs to follow a visually perceived boundary rather than an axis-aligned range.
- As a complement to rectangular brush in multi-view exploratory analysis tools.
- When the analytical question is “what about these specific points?” rather than “what about this range of X and Y?”
When NOT to Use
- When a rectangular brush suffices — lasso adds motor effort for no benefit if clusters are axis-aligned.
- On small mobile screens where precise freeform drawing is difficult with a finger.
- When the visualization has very few marks (under 10) — click-to-select with shift is simpler.
- When reproducibility matters — lasso shapes are hard to specify exactly and share.
- In dense views where the path would be occluded by the marks the user is trying to select.
How It Works
- The user activates lasso mode — either by holding a modifier key (e.g., Shift), clicking a toolbar icon, or as the default selection gesture.
- The user presses and drags across the chart, drawing a freeform path that follows the cursor.
- A visible stroke follows the cursor, giving real-time feedback on the shape being drawn.
- On mouse-up, the path is closed — a straight line connects the start and end points to form a closed polygon.
- The system performs point-in-polygon testing for every data mark, classifying each as inside or outside the lasso.
- Selected marks are highlighted; non-selected marks are dimmed. In linked views, the same data items are highlighted.
- The user can draw a new lasso to replace the current selection, or hold a modifier key to add to / subtract from it.
Variations
- Simple lasso: Freeform drawing that closes on mouse-up. The most common form.
- Polygon lasso: Click-to-place vertices, building a polygon point by point. More precise, less fluid.
- Magnetic lasso: The path snaps to perceived cluster boundaries or density contours. Rare in data visualization but used in image editing.
- Additive/subtractive lasso: Hold Shift to add a new region to the selection, or Alt/Option to subtract. Enables complex multi-region selections.
- Lasso with convex hull snap: The drawn shape is simplified to the convex hull of the selected points, cleaning up imprecise drawing.
Code Reference
// D3.js lasso selection on a scatterplot
const lasso = [];
let isDrawing = false;
svg.on("mousedown", (event) => {
isDrawing = true;
lasso.length = 0;
lasso.push(d3.pointer(event));
});
svg.on("mousemove", (event) => {
if (!isDrawing) return;
lasso.push(d3.pointer(event));
lassoPath.attr("d", d3.line()(lasso) + "Z");
});
svg.on("mouseup", () => {
isDrawing = false;
// Point-in-polygon test for each data mark
svg.selectAll("circle").style("opacity", d =>
pointInPolygon([x(d.xVal), y(d.yVal)], lasso) ? 1 : 0.1
);
});
function pointInPolygon([px, py], polygon) {
let inside = false;
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const [xi, yi] = polygon[i], [xj, yj] = polygon[j];
if ((yi > py) !== (yj > py) &&
px < ((xj - xi) * (py - yi)) / (yj - yi) + xi)
inside = !inside;
}
return inside;
}