BumbleTap Guide
Everything you need to master keyboard shortcuts and automation — the same guide that ships inside the extension.
Getting Started
BumbleTap lets you control any website with keyboard shortcuts and automated workflows. Here's the quick overview.
Bind any key combination to click elements, type text, trigger browser events, or run custom JavaScript.
Automated step sequences that run on page load, URL change, element appearance, or custom conditions.
Record your clicks, typing, and scrolling, then replay them as auto-actions.
Write code that runs when you press a key or when an action triggers. Full DOM access, async/await, helper functions.
Fuzzy-search all your bindings and run them instantly. Like Spotlight for your shortcuts.
Extract data from pages and pass it between steps using {{variableName}} syntax.
Quick Start
- Click the BumbleTap icon in your browser toolbar to open the popup.
- Click + Add Key Binding and record a key combination.
- Choose an action: click an element, type text, trigger a built-in event, or write custom code.
- Use the element picker (crosshair icon) to select elements on the page.
- Press your shortcut on the page to trigger the action.
Tip: Use the right-click context menu on any element to quickly create a binding for it.
Global vs Site Bindings
Site bindings only work on the site you created them for (e.g., youtube.com). Global bindings work on every site. Toggle between them using the globe icon in the popup.
Global bindings can exclude specific sites if needed. Site bindings always take priority over global bindings with the same key.
Keybindings
Each keybinding maps a key combination to an action. You can create single-key shortcuts or multi-key chord sequences.
Action Types
Simulates a click on a page element identified by CSS selector.
- Use the element picker to select any element visually
- Set a friendly element name for readability
- Use selector index to target the Nth match when multiple elements match
Inserts text into an input field. Two modes:
- In a textbox — target a specific field by CSS selector
- During typing — insert at current cursor position (requires modifier key)
Supports variable interpolation: {{date}}, {{clipboard}}, {{url}}, etc.
Options: Clear field first, Press Enter after typing.
Trigger browser/media functionality without writing code. Categories: Scroll, Media, Navigation, Zoom. See the Built-in Events page for the full list.
Execute JavaScript code with full page access and built-in helpers. See the Custom Code page for details.
First use: You'll need to enable User Scripts in Chrome. See Setup & Permissions.
Chord Shortcuts
Chords are multi-key sequences pressed one after another — like G then D. Unlike key combos (Ctrl+S), chords let you create hundreds of shortcuts using memorable letter sequences.
- Chord timeout (200–3000ms) — how long to wait between keys before resetting
- Chord overlay — optional visual indicator showing chord progress
- Configure both in the popup's Global Defaults settings
URL Scoping
Restrict when a binding activates by matching the current URL:
| Mode | Example | Use case |
|---|---|---|
| All pages | — | Always active on this site |
| Pattern | */video/* | Only on video pages |
| Regex | ^https://.*\.com/watch | Complex URL matching |
Use the Test URL button to verify your pattern matches the current page.
Built-in Events
Pre-built browser and media actions you can trigger without writing code.
Scroll
| Action | Description |
|---|---|
| Scroll Up / Down | Vertical scroll. Amount: small (100px), medium (300px), large (500px), full viewport, or custom pixels/percentage. |
| Scroll Left / Right | Horizontal scroll with same amount options. |
| Scroll to Top | Jump to the top of the page instantly. |
| Scroll to Bottom | Jump to the bottom of the page. |
Media
| Action | Description |
|---|---|
| Play/Pause | Toggle playback on the active video or audio element. |
| Mute/Unmute | Toggle audio mute. |
| Volume Up / Down | Adjust volume in 1–25% steps. |
| Seek Forward / Backward | Skip 5s, 10s, 30s, or a custom amount. |
| Fullscreen | Toggle fullscreen mode on the video. |
| Picture-in-Picture | Toggle PiP floating window. |
Media actions auto-detect the active video/audio. You can also target a specific element by CSS selector.
Navigation
| Action | Description |
|---|---|
| Go Back | Browser back button (history.back). |
| Go Forward | Browser forward button (history.forward). |
| Refresh | Reload the page. Optional confirmation dialog. |
Zoom
| Action | Description |
|---|---|
| Zoom In / Out | Scale the page in 5–50% increments. |
| Reset Zoom | Return to 100% (1x) zoom. |
Auto-Actions
Automated workflows that run sequences of steps when triggered. Each auto-action has a trigger (when to run), optional URL filter (where to run), and a list of steps (what to do).
Build workflows visually with drag-and-drop steps, inline previews, and one-click testing.
Click, Type Text, Wait, Custom JS, If/Else branching, and Extract data from the page.
Page load, delay, element visible, URL change, JS condition, or key press.
Extract data from elements and pass it to later steps using {{variables}}.
How It Works
- Open the popup on your target site and click + Add Auto-Action.
- Name your action and choose a trigger (when it should fire).
- Optionally set a URL filter to restrict which pages it runs on.
- Add steps — each step performs one operation in sequence.
- Click Test to try it on the current page, then Save.
Tip: Use the Macro Recorder to capture your clicks and typing, then convert them into auto-action steps automatically.
URL Filtering
Control which pages the action runs on:
- All pages — runs on every page of the site
- Pattern — wildcard matching, e.g.,
*/video/* - Regex — full regular expression, e.g.,
/watch\?v=/
See the sub-topics in the sidebar for details on each trigger type, step type, the Extract system, and If/Else branching.
Triggers
Triggers determine when your auto-action fires.
Fires when the page's DOM is fully loaded and parsed. This is the most common trigger — use it for actions that should run on every page visit.
Fires after a configurable delay (in milliseconds). Useful when you need to wait for dynamic content that loads after the DOM is ready.
Example: set to 2000 to wait 2 seconds after page load.
Fires when a specific element appears on the page. Uses polling to check periodically.
Great for: cookie banners, popups, or dynamically loaded content you want to auto-dismiss.
Fires when the URL changes without a full page reload. Essential for single-page apps (YouTube, Twitter, etc.) where navigation happens via JavaScript.
Fires when a custom JavaScript expression returns true. Polled periodically.
Example: document.querySelectorAll('.item').length > 10
Fires when a specific key combination is pressed. Like a keybinding, but triggers a full auto-action workflow.
Run modes: Once per page, Every press, or Once per session.
Step Types
Each auto-action consists of steps executed in order. Add steps via the + buttons in the editor.
Click
Click an element identified by CSS selector. Use the element picker to select it visually.
- Selector — CSS selector for the target element
- Element name — optional human-readable label
- Selector index — which match to click if multiple elements match (0 = first)
Text Input
Type text into a form field.
- Text content — what to type. Supports
{{variables}} - Clear first — select all existing text before typing
- Press Enter — submit after typing
Wait
Pause execution before continuing to the next step. Three modes:
| Mode | Description |
|---|---|
| Time delay | Wait a fixed number of milliseconds. |
| Element visible | Wait until a CSS selector matches an element on the page. Has a configurable timeout. |
| JS condition | Wait until a JavaScript expression returns true. |
Custom JS
Run arbitrary JavaScript code. Same helpers as keybinding custom code. Use ctx.get() / ctx.set() to read and write variables. See the Custom Code and ctx API pages.
Extract
Pull data from page elements into named variables for use in later steps. This is a powerful data-capture step — see Extract & Variables for full details.
If/Else
Conditional branching based on element presence or a JavaScript condition. See If/Else Branching for details.
Extract & Variables
The Extract step pulls data from page elements and stores it in named variables. Later steps can reference these variables using {{variableName}} syntax.
How to Use
- Add an Extract step in the auto-action editor.
- Enter a variable name (e.g.,
pageTitle). - Pick an element using the element picker or type a CSS selector.
- Choose what to extract (text, value, attribute, etc.).
- Click Preview to test the extraction on the current page.
- In later steps, use
{{pageTitle}}in text fields, orctx.get('pageTitle')in Custom Code steps.
Extraction Targets
| Target | Returns | Example use |
|---|---|---|
Text Content | Element's visible text | Read a heading, price, or label |
Input Value | Form field value | Read what's typed in a search box |
href | Link URL | Capture a link destination |
Attribute | Any HTML attribute | Read data-id, src, aria-label, etc. |
innerHTML | Element's inner HTML | Capture formatted content |
outerHTML | Full element markup | Capture element with its tags |
Computed Style | CSS property value | Read color, display, font-size |
Dimensions | Position & size object | Get width, height, top, left |
Regex Match | Pattern match from text | Extract numbers, IDs, or patterns from text |
Table/List | Array of row objects | Scrape tabular data into structured arrays |
Extract All Matches
Enable "Extract all matches" to capture data from every element matching the selector, not just the first.
- Result is always an array
{{prices}}joins with comma:10.00,25.00,7.50{{prices[0]}}accesses the first item:10.00{{prices[2]}}accesses the third item:7.50
Table Extraction
Extract structured data from HTML tables or repeated list elements:
// Configuration: Row selector: "table.prices tbody tr" Columns: product → "td:nth-child(1)" price → "td:nth-child(2)"// Result stored in variable: [ { product: "Widget A", price: "$10.00" }, { product: "Widget B", price: "$25.00" } ]
Transform
Apply a JavaScript expression to modify the extracted value before storing it. The raw value is available as value:
| Transform expression | What it does |
|---|---|
value.trim() | Remove leading/trailing whitespace |
parseInt(value) | Convert to integer |
value.replace('$', '') | Remove dollar sign |
value.split(',')[0] | Take first comma-separated part |
value.toUpperCase() | Convert to uppercase |
Safety: If the transform expression throws an error, the original untransformed value is kept. No data is lost.
Live Preview
Click the Preview button to run the extraction on the current page instantly. The result appears inline — green for success, red for errors. Transform is applied in the preview too.
Using Extracted Data in Custom Code
In text fields, use {{variableName}}. But in Custom JS steps, variables are accessed through the ctx API instead:
// Read a value set by a previous Extract step const title = ctx.get('pageTitle'); const price = ctx.get('prices[0]'); // array access// Write a new variable for later steps ctx.set('fullLabel', title + ' — ' + price);
All ctx operations are synchronous — no await needed. See the full ctx API reference for path syntax and lifecycle details.
If/Else Branching
Add conditional logic to your workflows. Run different steps based on whether an element exists or a JavaScript expression is true.
Condition Types
| Type | How it works |
|---|---|
| Element exists | Check if a CSS selector matches any element on the page. True if the element is found. |
| JS condition | Evaluate a JavaScript expression. True if it returns a truthy value. |
Branches
- Then — steps that run if the condition is true
- Else — steps that run if the condition is false (optional)
- Each branch can contain any step type: click, text, wait, JS, or even nested if/else
Example: Auto-dismiss Cookie Banner
IF element exists: "#cookie-banner .accept-btn" THEN: Click → "#cookie-banner .accept-btn" ELSE: Wait → 2000ms IF element exists: ".gdpr-overlay .dismiss" THEN: Click → ".gdpr-overlay .dismiss"
Lazy resolution: Variables set in earlier steps of a branch are available in later steps of the same branch. Branch substeps are resolved when they execute, not when the If step is encountered.
Custom Code
Write JavaScript that runs when you press a shortcut or when an auto-action step executes. Your code has full DOM access, async/await support, and a set of built-in helper functions.
Tip: You can paste this entire page into an AI chatbot and ask it to write code for you. Just describe what you want to automate!
Rules
awaitworks directly — no wrapper function needed- External libraries: add them via the Libraries panel on the custom-code step (fetched once at edit time, then cached offline)
- DOM access works normally — read, modify, create any HTML element
- Page JavaScript variables are not accessible (isolated execution context). Bundle libraries via the Libraries panel.
Examples
Click a button
$('.subscribe-button').click();
Wait for element, then click
const btn = await waitForElement('.cookie-accept'); btn.click(); toast('Done!', 'success');
Toggle video playback speed
const v = getActiveVideo(); if (v) { v.playbackRate = v.playbackRate === 1 ? 2 : 1; toast('Speed: ' + v.playbackRate + 'x'); }
Save data across page loads
let count = storage.get('clicks') || 0; count++; storage.set('clicks', count); toast('Clicked ' + count + ' times');
Use a bundled library
Attach canvas-confetti via the Libraries panel (it fetches once from a CDN and caches the bundle with the action). Then reference its export directly:
confetti({ particleCount: 200, spread: 90 });
Libraries are fetched at edit time and stored offline — actions keep working with no network access. Re-fetch to update a pinned version.
See Helper Functions and ctx API in the sidebar for the complete reference.
Helper Functions
Available directly in your custom code — no imports needed.
DOM Selection
| Helper | Description |
|---|---|
$(selector) | Find one element. Alias for document.querySelector() |
$$(selector) | Find all matching elements as an array. Alias for [...document.querySelectorAll()] |
findByText(text, tag) | Find an element by its visible text content. Optional tag filter (e.g., "button") |
waitForElement(selector, timeout) | Wait for an element to appear. Returns a Promise. Default timeout: 5000ms |
Media
| Helper | Description |
|---|---|
getActiveVideo() | Get the currently playing or visible <video> element |
getActiveAudio() | Get the currently playing <audio> element |
getMedia() | Alias for getActiveVideo() |
Async
| Helper | Description |
|---|---|
sleep(ms) | Wait for a duration. Usage: await sleep(1000) |
External JS libraries are added via the Libraries panel on the custom-code step (edit time, cached offline). The library's exports become available as globals inside your code.
Storage
| Helper | Description |
|---|---|
storage.get(key) | Read a saved value. Returns null if not found. Data persists across page loads. |
storage.set(key, value) | Save a value. Stored in localStorage with a BumbleTap prefix. |
storage.remove(key) | Delete a saved value. |
UI
| Helper | Description |
|---|---|
toast(message) | Show a notification popup. Default type: "info" |
toast(message, type) | Types: "success", "error", "info", "warning" |
Context Objects
| Object | Description |
|---|---|
binding | The keybinding object that triggered this code (keys, selector, action type, etc.) |
keyEvent | Info about the key press event: key, code, ctrlKey, shiftKey, etc. |
ctx API
In custom JS steps, the ctx object gives you synchronous read/write access to the shared variable pool.
Why ctx instead of {{}}? Custom code is NOT interpolated with {{...}} because it would conflict with JavaScript template literals. Use ctx.get() and ctx.set() instead.
API Reference
| Method | Description |
|---|---|
ctx.get(path) | Read a variable. Supports dot/bracket paths: ctx.get('items[0].name') |
ctx.set(name, value) | Write a variable. Available to all subsequent steps and actions. |
ctx.getAll() | Get a copy of all variables as a plain object. |
Examples
Read a variable from a previous Extract step
const title = ctx.get('pageTitle'); toast('Title: ' + title);
Set a variable for later steps
const count = $$('.item').length; ctx.set('itemCount', count); // Later steps can use {{itemCount}} in text fields
Read nested paths
// If a previous extract stored an object or array: const first = ctx.get('prices[0]'); // array index const name = ctx.get('user.name'); // dot path const deep = ctx.get('data[0].items[2].id'); // deep path
Lifecycle
- All
ctxoperations are synchronous — noawaitneeded - Variables are pre-loaded before your code runs and written back after it completes
- Variables persist across page navigations within the same browser session
- Variables are shared across all actions and tabs (cross-action by design)
- Variables clear when the browser closes
Variables
Use {{variableName}} in any text field to insert dynamic values. Variables can be built-in or created by Extract steps and custom code.
Built-in Variables
| Variable | Value | Example output |
|---|---|---|
{{date}} | Current date (localized) | 3/10/2026 |
{{time}} | Current time HH:MM | 02:30 PM |
{{url}} | Current page URL | https://example.com/page |
{{title}} | Page title | My Page Title |
{{random}} | Random 8-character string | a7f3b2x9 |
{{clipboard}} | Clipboard contents | whatever you copied |
User Variables
Create your own variables two ways:
- Extract step — pull data from page elements into a named variable
- ctx.set() — write a variable from custom JavaScript code
Path Syntax
Access nested data with dot notation and bracket indexes:
| Syntax | Resolves to |
|---|---|
{{myVar}} | The variable value (string, number, or comma-joined array) |
{{items[0]}} | First element of an array |
{{user.name}} | Nested object property |
{{data[0].items[2].id}} | Deep nested access |
Where Variables Work
- Text input steps and keybindings —
{{email}}in the text field - CSS selectors —
.item[data-id="{{itemId}}"] - Wait conditions — dynamic wait targets
- NOT in custom code — use
ctx.get()instead - NOT in variable names —
variableNameis always literal
Variables Sidebar
The auto-action editor has a Variables sidebar (toggle via the header button) that shows all current variables with their names, sources (which step created them), and values. It updates after every test run.
Missing variables: {{nonexistent}} resolves to an empty string, never throws an error. If used as a selector, the step will fail with "element not found."
Macro Recorder
Record your interactions — clicks, typing, scrolling, navigation — and convert them into auto-action steps.
How to Record
- Open the auto-action editor and click the Record button.
- A dark toolbar appears at the top of the page showing recording status.
- Interact with the page normally — your actions are captured as steps.
- Click Stop in the toolbar (or press the stop button) to finish.
- The recorded steps appear in the auto-action editor for review and editing.
What Gets Captured
| Interaction | Recorded as |
|---|---|
| Clicking elements | Click step with CSS selector and element name |
| Typing in fields | Text Input step with selector, text, clear/enter flags |
| Scrolling | Scroll step with direction and position |
| Page navigation | Navigate step with URL |
| Special keys (Enter, Tab, Escape) | Special-key step with modifiers |
Toolbar Controls
| Control | Shortcut | Description |
|---|---|---|
| Pause / Resume | — | Temporarily stop/restart recording |
| Undo | Ctrl+Z | Remove the last recorded step |
| Note | Ctrl+Shift+N | Add a text annotation to the last step |
| Decision Point | Ctrl+Shift+D | Mark a point where manual decisions happen |
| Step Feed | — | Expand to see all recorded steps in real-time |
| Stop | — | End recording and return steps to the editor |
Cross-page recording: If you navigate to a new page during recording, the recorder auto-resumes on the new page. Your steps are preserved.
Command Palette
A searchable overlay listing all your keybindings — site-specific and global. Find and execute any binding instantly.
How to Use
- Press the palette shortcut (configure in popup's Global Defaults under "Command Palette Shortcut").
- A search overlay appears. Start typing to filter bindings.
- Use ↑ ↓ arrow keys to navigate, Enter to execute, Esc to close.
Features
- Fuzzy search across binding names, selectors, element names, and custom code names
- Dimmed items — bindings whose target elements are missing on the current page appear dimmed
- Instant execution — run any binding without remembering its shortcut
- Shows both site-specific and global bindings applicable to the current page
Side Panel
BumbleTap ships with a Chrome side panel SPA — a dockable editor for actions, bindings, recording, and per-site settings. It opens alongside the page, or as a detached pop-out window.
Click the BumbleTap icon or press Ctrl+Shift+Y to open the side panel in the current window.
Click the pop-out icon to open as a floating window — useful for multi-monitor workflows.
Choose a binding mode so the panel stays focused on the tab or hostname you're working with, even as you switch tabs.
What's in the panel
- Home — site dashboard, scope switcher, recent activity.
- Auto-Actions — list, inline editor, run/test, logs, per-action variables.
- Keybindings — inline-expand rows for add/edit/delete/duplicate.
- Recorder — live capture + review before saving.
- Sites — manage every registered site from one place.
- Settings — playback, recording defaults, import/export, and binding mode defaults.
The sidebar is per window — each Chrome window has its own. Pop-outs are standalone and can follow a specific tab, a specific hostname, or the focused window.
Binding Modes
A binding mode controls which tab the side panel (or pop-out) reads bindings from and runs actions against. Change the default in Settings → Pop-out & Sidebar, or override per-instance via the pin button in the header.
| Mode | Behavior | Best for |
|---|---|---|
| Follow source window | Tracks the active tab of the window the panel opened from. Switching tabs changes which site the panel shows. | Everyday browsing; no commitment. |
| Pin to tab | Anchored to one tab ID. Follows that tab across navigations and even between windows. If the tab closes, an empty state with a re-pin button appears. | Editing one workflow while browsing other tabs. |
| Pin to hostname | Anchored to a site (e.g. youtube.com). Runs actions against the most recently active matching tab; opens a fresh tab if none is open. | Comparing or testing bindings across tabs of the same site. |
| Follow focused window | Retargets to whichever Chrome window is currently focused (debounced 250ms). Sidebars fall back to Follow source window. | Floating pop-out used across multiple windows. |
Action-lock
If an auto-action is running, the panel queues any incoming tab or mode changes until the run completes. Your action finishes against the hostname it started on — no mid-flight context shifts.
Edge cases
- Pinned tab navigates to a new site → the panel follows; the tab identity is the anchor.
- Pinned tab closes → empty state with a re-pin call-to-action.
- Pinned tab moves to another window → the panel follows to the new window.
- Tab is discarded (Chrome sleep) → auto-waked when an action runs.
- No tab matches a pinned hostname → a fresh tab is opened with the last-known URL.
- Browser restart with Pin to tab → the panel tries to re-pin to a tab whose URL matches; otherwise shows the empty state.
Per-instance override: Each pop-out tracks its own mode. Closing the pop-out discards its override — new pop-outs start from your global default.
Pop-out & Pinning
The header pin control lives next to the title: an icon button opens the mode menu, the pill next to it shows the current target (favicon + name). Click the pill to re-pin.
Opening a pop-out
- Open the side panel (Ctrl+Shift+Y or extension icon).
- Click the pop-out icon in the home header — a detached window spawns.
- The new pop-out starts in your global default mode (Settings → Pop-out & Sidebar).
Changing mode on a running pop-out
- Click the pin icon in the header (or press Ctrl+Shift+P).
- Pick a mode from the menu.
- For Pin to tab, click the target pill to choose which tab (current tab, or pick from a list).
Unsaved edits? If you're editing an action or binding when you change pin mode, BumbleTap asks before discarding the edit.
Multiple pop-outs
Each pop-out is independent. One can be pinned to youtube.com, another to a specific Jira ticket tab, a third in follow-window mode — they all work side by side.
Window title
Pop-outs update their window title with the pin target (e.g. BumbleTap — pinned to youtube.com). Handy when several pop-outs are in your taskbar.
Sidebars
The docked sidebar supports the same modes except Follow focused window (which is incoherent for a docked UI). Picking that mode in Settings has no effect on sidebars.
Keyboard Shortcuts
All panel-side shortcuts work in both the sidebar and the pop-out. They do not conflict with page-level bindings — BumbleTap binds them on the panel document only.
| Shortcut | Action |
|---|---|
| Ctrl+Shift+Y | Open / toggle the side panel (Chrome command — rebind in chrome://extensions/shortcuts) |
| Ctrl+K | Open the command palette |
| Ctrl+Shift+P | Open the pin / binding-mode menu |
| Ctrl+Shift+D | Copy a debug info dump to the clipboard (pin state, cached hostname, queued changes) — share with a bug report |
| Ctrl+S | Save the current action or binding |
| Ctrl+Enter | Run / test the current action |
| Ctrl+N | New action (from the actions list) |
| Ctrl+R | Record new action (from the actions list) |
| j / k | Navigate between step cards in the editor |
| e / Space | Expand / collapse the selected step |
| Esc | Dismiss dirty-state warnings, collapse expanded step, or go back |
The Ctrl+Shift+P shortcut works even when the pin switcher is not visible (e.g. inside a step editor) — the menu appears at the top-right.
Setup & Permissions
BumbleTap requires a one-time setup for custom code features.
Enable User Scripts (Required for Custom Code)
- Go to chrome://extensions in your browser.
- Find BumbleTap and click Details.
- Find "Allow in Developer Mode" (or "Allow User Scripts") and turn it ON.
- Click the reload button (↻) on BumbleTap, then refresh any open pages.
Note: This only needs to be done once. If custom code stops working after a Chrome update, repeat these steps.
Permissions Explained
| Permission | Why it's needed |
|---|---|
storage | Save your shortcuts and settings locally on your device |
unlimitedStorage | Store settings for many sites without hitting the 5 MB cap |
activeTab | Interact with the current tab when you use a shortcut |
scripting | Execute actions like clicking elements or running custom code |
userScripts | Run custom JavaScript in an isolated sandbox (CSP-exempt) |
tabs | Detect tab switches to load the right shortcuts |
webNavigation | Detect page navigation for auto-actions and SPA support |
contextMenus | Right-click menu for creating bindings and selecting elements |
clipboardRead | Support the {{clipboard}} variable in workflows |
sidePanel | Show the side panel editor alongside the page |
Host permissions | Allow shortcuts to work on any website you choose to configure |
Privacy
All your data stays on your device. BumbleTap has no servers, no accounts, and no analytics. Your settings are stored locally using Chrome's storage API. Nothing is ever transmitted.
See the Privacy Policy for full details.