Hiding an Electron window from screen share: 4 things setContentProtection doesn't tell you The team behind Natively, a desktop AI meeting assistant, documented four gaps in Electron's setContentProtection API that must be handled to keep an overlay window out of screen shares and recordings. Their approach applies protection across every live window, dedupes repeated calls to avoid Windows DWM churn, reasserts the macOS sharingType after app.dock.hide() resets it, and syncs skipTaskbar on Windows so the taskbar button doesn't leak into full-screen shares. The code ships in Natively's open-source repository. Natively https://heynatively.com is a desktop AI assistant for meetings and interviews. It listens to the call, transcribes it, and shows answers and notes in a small overlay. People use it while they share their screen, so the overlay has an obvious requirement: when the user turns on Undetectable mode, the overlay must not appear in the share or the recording. Electron gives you one line for this: win.setContentProtection true ; On macOS it sets the window's sharingType to NSWindowSharingNone . On Windows it calls SetWindowDisplayAffinity with WDA EXCLUDEFROMCAPTURE Windows 10 2004 and later . That line gets you most of the way. Here is what it took to get the rest, from code that ships in Natively today. An "overlay" in a real app is several windows: the answer panel, a pill, a toggle, a popover catcher, sometimes a launcher. If one of them is missed, that one shows up in the share. We keep a single method that walks every live window and pushes the same value: js private applyContentProtection enable: boolean : void { const windows = this.launcherWindow, this.overlayWindow, this.pillWindow, this.toggleWindow, this.popoverCatcher, ; windows.forEach win = { if win && win.isDestroyed win.setContentProtection enable ; } ; } Any window created later settings, model picker, screenshot cropper reads the current state when it is created, so a window opened mid-meeting doesn't slip through. Our setter was called from several places: the settings toggle, every overlay show, and a workaround around Win+Tab. On Windows, setting the same display affinity again and again churns DWM, and the window can go black or blank for a few hundred milliseconds. So the public setter does nothing when the value hasn't changed: public setContentProtection enable: boolean : void { if this.contentProtection === enable return; this.contentProtection = enable; this.applyContentProtection enable ; } This one cost us the most time. Undetectable mode also hides the app from the Dock with app.dock.hide . That flips the app's activation policy, and WindowServer re-evaluates every window. It can quietly reset sharingType to the default, which means the overlay is visible in the share again. Your in-memory flag still says true , so the deduped setter above would skip the call. The fix is a second method that skips the dedupe and pushes the current value to the OS again: public reassertContentProtection : void { this.applyContentProtection this.contentProtection ; } Call it after every app.dock.hide / app.dock.show . On macOS, hiding from the Dock removes the app's visible presence. On Windows, a window that is excluded from capture can still have a taskbar button with the app's name on it, and the taskbar is part of a full-screen share. Most of our windows are created with skipTaskbar: true . The launcher is the exception because in normal mode it is the main window, so we sync it with the mode: public syncLauncherTaskbarForStealth : void { if process.platform == 'win32' return; this.launcherWindow?.setSkipTaskbar this.appState.getUndetectable ; } The macOS path doesn't use this at all. Forcing skipTaskbar there would change behaviour on a platform that handles it through the Dock. Content protection is a request to the OS compositor, and different capture APIs respect it differently. Modern APIs ScreenCaptureKit on macOS, Windows Graphics Capture honour it. Some older capture paths may not. So we test with the apps people really share from Zoom, Google Meet in Chrome, Teams, OBS, the system screenshot tool on both platforms, with the mode on and off, and we treat "it was hidden on my machine" as one data point, not proof. All of this is in Natively's source on GitHub: Natively-AI-assistant/natively-cluely-ai-assistant https://github.com/Natively-AI-assistant/natively-cluely-ai-assistant , in electron/WindowHelper.ts . Natively runs on macOS and Windows and is free for personal use. You can bring your own AI keys or run models locally with Ollama. If you're building something with an always-on-top overlay, I hope this saves you an afternoon. Download and docs: heynatively.com https://heynatively.com