I Spent a Weekend Teaching My Browser What Hand Gestures Mean
february 27, 2026
A completely normal story about building a mouse-gesture Chrome extension because the back button was, apparently, too far away.
Dev Story · Projects · Chrome Extension · TypeScript · Manifest V3 · Browser
I Spent a Weekend Teaching My Browser What Hand Gestures Mean
My right hand is 4cm from the back button. I built an entire Chrome extension instead of moving it.
Normal people use the back button. They click it. They go back. They continue their lives. I, apparently, am not normal people.
It started with a simple thought
"I wish I could just swipe to go back."
That's it. That's the entire origin story. I didn't have a problem. I manufactured one. Then I built a solution for the problem I manufactured, added five build tools, a gesture detection state machine, and named the whole thing after a Japanese cat reference.
Totally fine. Very normal behavior.
Manifest V3: A Chrome Story
Ah yes, Manifest V3 — Google's gift to extension developers who thought their job was too easy. You know what's fun? Having to write a service worker for background tasks that does nothing but switch tabs. You know what's more fun? Discovering that your popup can't run inline scripts because of Content Security Policy, so you now need a custom build tool that literally extracts your inline scripts into separate files at compile time.
That's not a workaround. That's archaeology.
But sure, it's totally fine, it's just one extra script, it'll take like 10 minutes.
// extract-inline.ts — the file that exists because CSP said no
// 74 lines of "why am I like this"// extract-inline.ts — the file that exists because CSP said no
// 74 lines of "why am I like this"I'm fine. Everything is fine.
"I'll just write a quick content script"
The content script was supposed to be simple. Listen for right-click drag. Detect direction. Send message. Done.
What actually happened:
- A canvas element gets injected into every single page on the internet
- High-DPI support because god forbid the gesture trail look blurry on a retina display
- Quadratic curve smoothing so the trail looks "fluid"
- Real-time direction labels with arrow symbols rendered mid-gesture
- A separate
gesture.tsfile with a 300-degree threshold circle detection algorithm usingatan2cross-products
For going back. And forward. Like the buttons that are RIGHT THERE.
The Circle Gesture
Okay look. I need to talk about the circle detection.
So for a circle gesture, I needed to track whether the mouse was moving in a loop. The naive approach: did the path close on itself? Sure. Done. Ship it.
What I actually did: accumulated signed angle changes via atan2 cross-products, checked if the total exceeded 300 degrees of rotation, verified path closure within 80px.
This is for the "refresh page" gesture. The button is F5. It has been F5 for 30 years. My circle detection algorithm is more sophisticated than most of the code at my last job.
Using Astro for a Browser Popup
The popup is one page. It shows a gesture reference table. It is 400px wide. It will never be server-side rendered. It has no dynamic data. It will never need hydration.
I used Astro.
It has a full design system. Custom CSS variables. A sage-green color palette with names like sage-300 and sage-600. A cubic-bezier easing curve: (0.22, 1, 0.36, 1). I have named shadow tokens. For a popup.
--shadow-soft: 0 4px 16px rgba(46, 47, 44, 0.04);
--shadow-card: 0 2px 8px rgba(46, 47, 44, 0.06);--shadow-soft: 0 4px 16px rgba(46, 47, 44, 0.04);
--shadow-card: 0 2px 8px rgba(46, 47, 44, 0.06);For. A. Popup.
The Build Pipeline
It's a Chrome extension. You write some JS, you load it unpacked, you test it. Simple.
My build pipeline:
- Astro compiles the popup
- Bun bundles the content and background scripts
- Custom CSP extraction script runs
- Icon generator (Sharp) creates four sizes from source
- Pack script ZIPs everything up for distribution
That's five tools. For an extension that does seven things: go back, go forward, next tab, previous tab, scroll top, scroll bottom, refresh.
I wrote more build infrastructure than I wrote actual features. That's my brand now apparently.
The Name
TabiNeko. "Tabi" (旅) means journey. "Neko" (猫) means cat. It's a traveling cat. It navigates. Get it?
I named my mouse gesture extension after a traveling cat because:
- The gestures feel like a cat pawing at the screen
- I wanted the branding to be "Japanese minimalist"
- The color is sage green which is calming
- I am extremely normal
The tagline is "Right-click. Swipe. Flow." I spent longer on the tagline than I did on the background service worker.
Does It Even Work?
Yes. Actually, it works great. Right-click drag to the right then down for next tab. Left-click drag left then up for previous. Draw a circle to refresh. It's smooth, it feels good, and the gesture trail looks genuinely satisfying.
Did I need it? No. Is the back-button 4cm away? Yes. Am I using TabiNeko every single day now?
...yes.
The gestures are faster than I expected once you muscle-memory them. The tab switching is legitimately more fluid than Ctrl+Tab. The circle-to-refresh has ruined me for F5. I created a problem, then I solved it better than the original solution ever could.
That's just engineering, bro.
TabiNeko is open source if you want to try it. Or you can just use the back button. Both are valid life choices. I won't judge you. I'm too busy drawing circles on my screen to refresh things.