If you follow along with Udemy courses while coding in VS Code (or any other editor), you've probably hit this annoyance: the F7/F8/F9 media keys don't work properly with the Udemy video player. Play/pause fights with itself, and skip forward/back do nothing at all.
Here's why, and two ways to fix it.
The Problem
Udemy's player has a function called syncPlayStateToPlayer that constantly tries to reconcile its internal play/pause state with the actual video element. When you press F8 (play/pause) from another application, the browser's Media Session API toggles the video - but then Udemy's sync function fires and reverts it. The result: nothing happens, or the video flickers.
F7 and F9 (previous/next track) simply aren't wired up at all.
Fix 1: Chrome DevTools Local Override
This is the "proper" local hack. It patches the source file directly so the fix survives page navigation within the course.
- Open DevTools (or Safari's Web Inspector)
- In the Sources panel, search for a file starting with
course-taking-udlite-app...js - Right-click the file and select Override content (Chrome) or add it to Local Overrides
- Find the function
syncPlayStateToPlayerand addreturnas the first statement:
this.syncPlayStateToPlayer = () => {
return // <-- added: short-circuits the sync
clearTimeout(this.playStateSyncTimeout);
try {
if (!this.playingStateNeedsSyncing) {
return
}
if (this.isVideoPlayerInitialized && this.store.isSourceReady) {
this.isPlaying ? this.play() : this.pause()
}
this.playStateSyncTimeout = setTimeout(this.syncPlayStateToPlayer, O.Yj)
} catch (e) {}
}
The early return kills the sync loop entirely. The rest of the function becomes dead code.
Fix 2: Console Snippet (Recommended)
This is lighter and doesn't require digging through minified source. It does three things:
- Walks the React fiber tree from the
<video>element to find the component instance - Replaces
syncPlayStateToPlayerwith a no-op - Registers Media Session handlers for F7 (rewind) and F9 (skip forward)
(() => {
const v = document.querySelector('video');
const k = Object.keys(v).find(k => k.startsWith('__reactFiber$'));
let f = v[k];
while (f) {
const i = f.stateNode;
if (i && i.syncPlayStateToPlayer) {
i.syncPlayStateToPlayer = () => {};
console.log('Patched');
break;
}
f = f.return;
}
navigator.mediaSession.setActionHandler('previoustrack', () => {
document.querySelector('[data-purpose="rewind-skip-button"]')?.click();
});
navigator.mediaSession.setActionHandler('nexttrack', () => {
document.querySelector('[data-purpose="forward-skip-button"]')?.click();
});
})();
How to save it
In Chrome DevTools, go to Sources > Snippets, create a new snippet, paste the code, and save. Run it once after each page load (right-click the snippet > Run, or Ctrl+Enter).
Why This Matters
The real win is workflow. If you're coding along with a Udemy course in VS Code, you can control playback without switching to the browser tab:
- F8 - play/pause (works after patching the sync)
- F7 - skip back (mapped to Udemy's rewind button)
- F9 - skip forward (mapped to Udemy's forward button)
No alt-tabbing. No hunting for the browser. Just press a key and keep coding.
Caveats
- The snippet needs to be re-run on each full page refresh (navigating between lectures within a course usually doesn't require it)
- This relies on Udemy's current React internals and
data-purposeattributes - it could break if they refactor - The local override approach is more persistent but tied to the specific JS bundle filename, which changes on deploys
Top comments (0)