Your YouTube screenshot is black because you screenshotted the wrong layer
You hit PrintScreen on a video, paste, and get a black rectangle where the picture was. The windowframe is there. The controls are there. The video is a hole. The video was never in the pixels you copied. What. . .

You hit PrintScreen on a video, paste, and get a black rectangle where the picture was. The windowframe is there. The controls are there. The video is a hole. The video was never in the pixels you copied. What actually happenedA hardware-accelerated video does not get painted into the same surface as the rest of the window. The decoder hands frames to the GPU, which composites them into an overlay plane at the very laststep, after the window contents have been assembled. A screen capture that reads the window surfacereads what is underneath that plane, which is the punch-out colour: black. This is why the same PrintScreen works fine on some machines and fails on others, why it startsfailing after a GPU driver update, and why "disable hardware acceleration" is the advice that keepsgetting handed out. Turning off acceleration does fix it, by making the video render into the normalpaint path, along with the battery cost. The better move is to stop capturing the window and ask theelement for the frame instead. Grab the frame from the elementdrawImageaccepts aand reads the frame it is currently showing:constv=document. querySelector("video");constc=document. createElement("canvas");c. width=v. videoWidth;// NOT v. clientWidthc. height=v. videoHeight;c. getContext("2d"). drawImage(v,0,0);c. toBlob(b=>open(URL. createObjectURL(b)),"image/png");Enter fullscreen modeExit fullscreen modePaste that into the console on a YouTube tab and you get the exact frame, no black. The one line worth staring at isv. videoWidth. That is the resolution of the decoded stream, whichis what you want. clientWidthis the size of the CSS box, so using it silently downsamples a 4Kframe to whatever your player happens to be sized at in that window. This is the difference between ausable still and a soft 640-pixel-wide one, and it is easy to get wrong because both produce an image. Set the player to 2160p first if you want 2160p out. videoWidthreports the stream YouTube iscurrently feeding you, not the best one available. Straight to the clipboardSkipping the file is often what you want, since the frame is usually headed for a slide or a note:constpng=awaitnewPromise(r=>c. toBlob(r,"image/png"));awaitnavigator. clipboard. write([newClipboardItem({"image/png":png})]);Enter fullscreen modeExit fullscreen modeTwo constraints that will bite you. The clipboard only takes PNG here, so if you produced a JPEGblob you have to re-encode before writing. Andclipboard. writeneeds a user gesture and a focuseddocument, so it works from a click handler and fails from asetTimeout. When it still comes out blackdrawImageon a DRM-protected video throws instead of returning pixels. Widevine-protected content —a rented film, most paid streaming — is out of reach by design, and no extension or console snippetchanges that. YouTube's ordinary videos are not protected, so they are fine. Wrap it, because an unhandled throw here looks identical to a broken tool:try{ctx. drawImage(v,0,0);}catch{/* protected stream */}Enter fullscreen modeExit fullscreen modeNaming the fileIf you are saving rather than copying, put the timestamp in the name while you still have it:constt=Math. floor(v. currentTime);conststamp=`${String(t/3600|0). padStart(2,"0")}-${String(t/60%60|0). padStart(2,"0")}-${String(t%60). padStart(2,"0")}`;Enter fullscreen modeExit fullscreen modeFrames without a timestamp in the filename are unfindable a week later, which anyone who has takenlecture notes this way has learned the hard way. The extensionI packaged this asScreenshot for YouTube — Video FrameCapture. Disclosure:mine, free, no account, no network requests. It puts a camera button in the player controls, binds Alt+S, saves PNG or JPEG or copies to theclipboard, and names files with the video title plus the timestamp above. There is a popup capturebutton as a fallback for Shorts and embeds, where the standard control bar is absent. Getting thebutton to survive YouTube's SPA navigation needed aMutationObserverplus theyt-navigate-finishevent, which is the least interesting and most fiddly part of the whole thing. If you only need one frame today, the console snippet above is the entire mechanism. The extensionexists because the twentieth time you do it by hand,videoWidthis not the thing you want to beremembering.