Releases: igrigorik/videospeed
v0.10.2
What's Changed
Keyboard shortcuts now work with virtual input tools (wtype, AutoHotkey) and non-QWERTY layouts. The speed state machine was overhauled: per-site speed rules now always win on fresh page load (global remembered speed no longer leaks across sites), the Reset key properly sticks on sites with custom speed rules, and lifecycle events like tab backgrounding can no longer corrupt your remembered speed. The controller visibility system was rewritten so "hide by default" is a hard preference that never flashes, while normal mode correctly follows YouTube's autohide and flashes on speed changes. Saving options no longer reinitializes every open tab. CSS positioning on YouTube was fixed for the paid promotion overlay, and a performance regression on Gemini (caused by style attribute selectors forcing global invalidation) was resolved. x.com is no longer blocked by default.
- fix(css): handle both controller insertion points on YouTube (fe003f9)
- fix(speed): null semantics for lastSpeed, per-site rules win on load (12a4fcd)
- fix: lifecycle restore must not corrupt remembered speed (#1494) (15d575c)
- fix(bridge): scope lifecycle events to popup toggle (0761e06)
- fix(visibility): rewrite flash guards, remove autohide override (d96ea5a)
- fix(youtube): prevent player crash (04232b1)
- fix(css): sibling-aware controller positioning, strip dead domain rules (8305f56)
- fix(css): separate default/custom CSS, fix Gemini (054b4a0)
- fix: stop swallowing ratechange events in passive observer paths (f876652)
- remove migration warning (f338993)
- fix(keyboard): restore event.code matching, fix layout display with getLayoutMap() (6762e2e)
- remove x.com from default blocked sites (a6630cb)
- fix: ignore legacy blacklist when siteRules exists (daf53da)
v0.10.1
Interaction tracking restores ability to use player's own speed controls and shortcuts, while still fighting back against automatic resets. Numpad Enter is now a fully distinct, assignable key (shown as "Num Enter" in the shortcuts editor), giving numpad users a dedicated binding separate from the main Enter key. The options page also gets a visual polish pass: the shortcuts remove button is now properly centered, and the settings header stays fixed while you scroll.
v0.10.0
0.10.0 is a significant release covering three major areas: a full extension architecture overhaul, speed control improvements, and developer infrastructure.
The biggest change is a ground-up rearchitecture of how the extension injects code into pages. The old bridge worked by injecting <script> and <style> DOM elements into <head> — a technique that caused intermittent player crashes on sites like YouTube that use internal MutationObservers during framework initialization. 0.10.0 replaces this entirely with Chrome's MV3 world:"MAIN" content script primitive (Chrome 111+), which evaluates the extension's JavaScript directly in the page's V8 context with zero DOM mutations. A thin ISOLATED world bridge (content-bridge.js) handles all chrome.* API access and communicates via CustomEvents on document.documentElement. Controller CSS is injected via document.adoptedStyleSheets rather than a <style> element — another zero-mutation API. Together these changes eliminate the entire class of extension-triggered rendering crashes. web_accessible_resources is also removed as inject.js no longer needs to be web-fetchable, closing an extension fingerprinting vector.
Speed control has been substantially improved. Fight-back logic (how the extension handles sites that reset playback speed) was redesigned with exponential backoff, replacing the brittle forceLastSavedSpeed approach. Speed increment/decrement now snaps to 1.0× when crossing the boundary, and drag-to-speed uses unified pointer events for mouse and touch. Keyboard bindings migrated from deprecated keyCode to event.code with modifier chord support. New per-site speed defaults let you define a baseline speed per domain via site rules, and the blacklist/enable lifecycle is now reactive — disabling the extension or adding a site to the blacklist tears down controllers immediately without a reload. The options page gained settings export/import, a user-editable controller CSS editor with syntax highlighting, and dirty-state feedback on save.
On the infrastructure side: the full test suite was migrated from a custom runner to Vitest (348 tests), GitHub Actions CI was added with lint + build + test on every push, release packaging is now reproducible with npm run release, and the codebase moved to ESLint flat config with husky + lint-staged pre-commit hooks.
Architecture
- refactor: replace DOM injection bridge with MV3 world:"MAIN" content scripts (93c1e3a)
- fix: use document_idle for inject.js to close settings handshake race (10d0037)
- Remove activeTab permission; make background sole icon authority (#1464)
- Restrict page→content bridge to lastSpeed writes only (#1463)
Speed control
- Separate speed user-intent from playback state (#1434)
- Redesign ratechange fight-back: exponential backoff, remove forceLastSavedSpeed
- feat: snap to 1.0× when speed increment/decrement crosses the boundary (#1438)
- feat: migrate keyboard identity from keyCode to event.code + modifier chord support
- feat: per-site speed defaults via unified site rules (#1444)
- Replace per-key force flag with global exclusiveKeys setting
- Fix reactive lifecycle for blacklist and enable/disable — teardown without reload (#1435)
Options page & UX
- feat: user-editable controller CSS with live preview
- CSS syntax highlighting in controller CSS editor
- Add settings export/import (#1445)
- Unify drag handling for mouse + touch; double-click to reset speed (#1436)
- Wheel scroll: two-layer filtering to prevent accidental speed changes (#1442)
- dirty-state feedback on options save button
- Remove close (×) button from on-video controller (#1460)
- Add toggle behavior to jump-to-marker action (#1439)
Bug fixes
- fix: restore reset/preferred speed cross-toggle (#1353)
- fix: guard against double-injection of content script (#1406)
- Skip speed overlay on gif-like video elements (#1461)
- fix: drop customElements.define() to eliminate es5-adapter conflict (#1458)
- fix: controller position on Dailymotion
- fix: YouTube embedded controller
- fix race condition: write only changed storage keys
Infrastructure
v0.7.1
I am re-releasing this in attempt to rerun v0.7.1 through github actions in hopes it will upload to the Chrome store.
What's Changed
- Adopt Manifest V3 by @dotproto in #867
- Fix positioning for videos with non-zero offsetTop/offsetLeft by @russelldavis in #854
- Update to work with the latest Apple TV by @nathanielherman in #866
New Contributors
- @dotproto made their first contribution in #867
- @russelldavis made their first contribution in #854
- @nathanielherman made their first contribution in #866
Full Changelog: v0.6.5...v0.7.1