Command Menu

Search pages, blogs, projects, and more...

february 27, 20264 min read

TabiNeko: Teaching Chrome That a Wrist Flick Means "Go Back" Was Harder Than It Sounds

A completely normal story about building a mouse-gesture Chrome extension because the back button was, apparently, too far away.

Dev StoryProjectsChrome ExtensionTypeScriptManifest V3Browser

TabiNeko: Teaching Chrome That a Wrist Flick Means "Go Back" Was Harder Than It Sounds

My right hand is 4cm from the back button. I built an entire Chrome extension instead of moving it.

It started with a reasonable thought.

"I wish I could just swipe to go back."

That's it. No technical problem. No user pain. Just a fleeting preference about mouse navigation that a normal person would think once and immediately forget. I, instead, spent a weekend building a gesture recognition engine, a canvas-based trail renderer, a Manifest V3 service worker, and a full design system for a 400px popup.

The back button is still 4cm away. I've just stopped using it.

Manifest V3: Google's Gift to Extension Developers Who Thought Life Was Too Easy

You know what Manifest V3 took away? The ability to run persistent background pages. Fine, that's a service worker now. You know what else it took? Inline scripts in the popup HTML, because Content Security Policy says no.

That second one meant I needed a build step that extracts inline scripts into separate files at compile time. Not a workaround. That's just the job now. I wrote a custom extract-inline.ts script — 74 lines — that exists entirely because Chrome's CSP decided my popup was suspicious.

I'm fine. The script is fine. We're all fine.

"I'll Just Write a Quick Content Script"

Famous last words.

The content script injects into every page on the internet. It listens for right-click drag events. It detects direction. It sends a message to the background service worker. Done.

What actually ended up in the content script:

A canvas element gets injected into every page. High-DPI support, because gesture trails on a retina display need to be crisp. Quadratic curve smoothing so the trail looks fluid instead of jagged. Real-time cardinal direction labels rendered mid-gesture with arrow symbols. A gesture.ts module with direction detection via accumulated angle deltas.

For going back. And forward. And switching tabs. And scrolling to the top.

The Circle Gesture Problem

I need to talk about circle detection for a moment.

The naive approach: did the mouse path close on itself? Easy. Done. Ship it.

What I actually implemented: accumulated signed angle changes using atan2 cross-products across consecutive path points, checked whether the total exceeds 300 degrees of arc, verified path closure within 80 pixels of the starting point.

This is for refreshing the page. The keyboard shortcut is F5. It has been F5 for thirty years. My circle detection algorithm is more sophisticated than my first three jobs combined.

I have no regrets.

The Gesture Map

Seven gestures. Seven actions.

GestureAction
Swipe LeftGo Back
Swipe RightGo Forward
Swipe UpScroll to Top
Swipe DownScroll to Bottom
Right then DownNext Tab
Left then UpPrevious Tab
CircleRefresh Page

All triggered by right-click drag. No accidental fires on normal right-click. The direction detection only commits once you've moved enough to make intent clear.

The Build Pipeline That Grew a Pipeline

It's a Chrome extension. You write JS, load it unpacked, test it. Simple.

My build pipeline:

  1. Astro compiles the popup
  2. Bun bundles the content and background scripts
  3. Custom CSP extraction script extracts inline scripts
  4. Sharp generates four icon sizes from source
  5. Pack script ZIPs everything for distribution

Five tools. For seven features. I wrote more build infrastructure than I wrote browser logic. That's my brand.

The Popup Design System

The popup is 400px wide. It shows a gesture reference table. It will never need server-side rendering. It has no dynamic data whatsoever.

I gave it a full design system. Custom CSS variables. A sage-green palette with named tokens like sage-300 and sage-600. Named shadow tokens. A cubic-bezier easing curve. In Astro.

css
--shadow-soft: 0 4px 16px rgba(46, 47, 44, 0.04);
--shadow-soft: 0 4px 16px rgba(46, 47, 44, 0.04);

For a popup.

Does It Actually Work?

Yes. Surprisingly well.

The gesture trail is smooth. The direction detection is accurate. Switching tabs with a two-direction swipe is genuinely faster than Ctrl+Tab once you've muscle-memorized it. The circle-to-refresh has completely replaced F5 in my daily workflow.

I created a problem, solved it more thoroughly than necessary, and now can't imagine browsing without it. Standard engineering outcome.

TabiNeko is open source. You can use the back button instead. Both are valid. I won't judge you. I'm too busy drawing circles on web pages to have opinions about other people's navigation choices.

Grab it on GitHub.