HumanSignal / HumanSignal/label-studio

[ux] Brush cursor size does not match painted stroke size

Open
#9,356 3 comments 2 reactions 0 assignees View on GitHub
community_bug
Dominant language
TypeScript
Stars
28.3k
Forks
3.7k
Avg merge
14h
Merged PRs (30d)
15

Description

**Describe the bug**
When labeling an image with the brush tool, the white circular cursor that indicates brush size is smaller than the actual painted stroke. The mismatch is noticeable when clicking/dabbing; the colored area is larger than the cursor preview.

**To Reproduce**
Steps to reproduce the behavior:
1. Open an image labeling project that uses Brush/BrushLabels for segmentation.
2. Select the Brush tool.
3. Set a brush size (e.g., 5) using the slider.
4. Click once to paint a dot.
5. Compare the painted dot size to the cursor's white circle.

**Expected behavior**
The painted dot should match the cursor preview size (the white circle).

**Screenshots**
Image

**Environment:**
- OS: macOS 14.4.1
- Label Studio Version 1.20.0
- Browser: Chrome 144.0.7559.97
- Zoom level: any

**Additional context**
The cursor size appears to be based on the raw brush size, while the actual stroke size is scaled by zoom, which may cause the mismatch when zoomed.

I found the cursor sizing and the actual paint size, and they’re using different scaling. That’s likely why the painted stroke looks larger than the white circle, especially when zoom isn’t 1.

Cursor size is defined in canvas.js (line 357) (createBrushSizeCircleCursor) and used in Brush.jsx (line 233) with self.strokeWidth directly.
Actual brush stroke width is applied in BrushRegion.jsx (line 308) as pathPoints.strokeWidth * self.scaleX * self.parent.stageScale, which scales by zoom.
To make them match, you can scale the cursor size by the same factor (at least stageScale, and possibly scaleX if you see distortion) and refresh the cursor on zoom changes. Example direction: in Brush.jsx (line 233), use self.strokeWidth * (self.obj?.stageScale ?? 1) and ensure updateCursor() runs when zoom changes (e.g., after handleZoom/updateImageAfterZoom). Note the cursor has a browser max size (~128px), so very large zooms may still cap out.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.