DEV Community

Satoyoshi
Satoyoshi

Posted on

A 20-year-old HCI paper, resurrected as a Chrome extension

I missed the tiny "x" on a browser tab again today. Meant to close it, switched to it instead. Aiming a one-pixel pointer at an eleven-pixel checkbox is basically microsurgery, and somewhere along the way we all just accepted that.

Here's the strange part: HCI research solved this twenty years ago. It just never shipped.

The paper

In 2005, Grossman and Balakrishnan published The Bubble Cursor at CHI. The whole idea fits in one sentence:

Make the cursor's hit area a dynamic circle that always contains exactly one target.

That turns out to be the same thing as always selecting the target nearest to the pointer. Picture the screen divided into Voronoi cells, one per clickable thing, and the cursor picking the owner of whatever cell it's currently in.

The clever part is what it refuses to do. Naive "gravity" cursors snap to every link on the way to the one you actually want, and they get stuck. The bubble cursor grabs exactly one target by definition. The moment a second target becomes nearer, it switches. So it stays calm on link-dense pages, and the paper showed significant speedups in controlled experiments.

Twenty years later our cursors are still naked, so I built it as a Chrome extension. It's called MagPoint.

The core is about 30 lines

A content script collects clickable elements (a[href], button, input, ARIA roles and so on) and, every frame, picks the one with the smallest point-to-rectangle distance:

function pointToRect(x: number, y: number, r: DOMRect): number {
  const dx = Math.max(r.left - x, 0, x - r.right);
  const dy = Math.max(r.top - y, 0, y - r.bottom);
  return Math.hypot(dx, dy);
}
Enter fullscreen mode Exit fullscreen mode

Clicks that land in the empty space near a captured element get re-routed to it. Past a max radius of 120px the magnet lets go, and empty-space clicks behave like the normal web. It also stands down while you type or select text, because getting yanked toward a link mid-sentence would be infuriating.

The rule that kept me sane: the visuals never decide

The scary failure mode for a magnetic cursor is the visuals and the hit test disagreeing. So the architecture has one hard rule: selection is pure math, and the animation only reads it.

The captured element wears a liquid-glass outline that stretches toward your pointer. It's a single deformable path where each outline point gets lifted by its excess distance to the pointer:

bulge_i = A * exp(-(d_i^2 - d_min^2) / 2σ^2) * proximity
Enter fullscreen mode Exit fullscreen mode

My first version did something subtly different: it centered a Gaussian bump on the single nearest outline point. That point teleports to the opposite side of the outline the instant you cross the element's midline, and the whole bulge snaps along with it. Liquids don't teleport. Removing the discrete "which point?" choice from the formula made the deformation continuous everywhere.

None of this can mis-click, though, because the glass never writes to the selection. At one point I threw away the entire visual layer (canvas out, SVG in) without touching click behavior at all.

The thing I didn't plan to build

I'm bad at recording demos by hand, so the repo also contains a small pipeline: load the extension into headless Chromium, inject a fake macOS cursor that follows scripted bezier paths, record via CDP screencast at 60fps, then cut the video with ffmpeg. One command, infinite retakes.

Two traps from that rabbit hole, so you don't fall in:

  • Branded Chrome 137+ silently ignores --load-extension. No error, nothing. Playwright's Chromium (channel: 'chromium') still works.
  • Recording real sites means recording other companies' ads. They hydrate late, rotate every load, and often carry no DOM text, so a one-shot cleanup loses. A sweeper that keeps running every 300ms during capture wins.

Try it

"Clicking the gap and hitting the right thing anyway" is one of those sensations you can't un-feel.

If you have opinions on pointing assistance, or a favorite twenty-year-old paper that deserves to ship, I'd love to hear them.

Top comments (0)