<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: GianlucaBave</title>
    <description>The latest articles on DEV Community by GianlucaBave (@gianlucabave).</description>
    <link>https://dev.to/gianlucabave</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4224678%2F6703153e-006d-4b9b-b637-fb266d06157d.jpg</url>
      <title>DEV Community: GianlucaBave</title>
      <link>https://dev.to/gianlucabave</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gianlucabave"/>
    <language>en</language>
    <item>
      <title>How I made a window invisible to screen sharing on macOS (and checked that it really is)</title>
      <dc:creator>GianlucaBave</dc:creator>
      <pubDate>Tue, 03 Nov 2026 16:43:36 +0000</pubDate>
      <link>https://dev.to/gianlucabave/how-i-made-a-window-invisible-to-screen-sharing-on-macos-and-checked-that-it-really-is-png</link>
      <guid>https://dev.to/gianlucabave/how-i-made-a-window-invisible-to-screen-sharing-on-macos-and-checked-that-it-really-is-png</guid>
      <description>&lt;p&gt;I'm building &lt;a href="https://github.com/GianlucaBave/murmury" rel="noopener noreferrer"&gt;Murmury&lt;/a&gt;, an open-source Mac app that listens to the other side of a video call and shows short answers from your own documents in a small floating window. The whole point is that the window is for you. When you share your screen, the other people should see your slides, not your notes.&lt;/p&gt;

&lt;p&gt;macOS has a one-line way to do this. The interesting part was proving it actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one line
&lt;/h2&gt;

&lt;p&gt;Every &lt;code&gt;NSWindow&lt;/code&gt; has a &lt;code&gt;sharingType&lt;/code&gt;. Set it to &lt;code&gt;NSWindowSharingNone&lt;/code&gt; and macOS leaves the window out of screen captures. Murmury's UI is PySide6 (Qt), so I reach the native window through PyObjC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;objc&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;AppKit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSFloatingWindowLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NSWindowCollectionBehaviorCanJoinAllSpaces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;NSWindowCollectionBehaviorFullScreenAuxiliary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NSWindowSharingNone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;objc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;objc_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_void_p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;winId&lt;/span&gt;&lt;span class="p"&gt;())).&lt;/span&gt;&lt;span class="nf"&gt;window&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHidesOnDeactivate_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# a Qt tool window is a panel, macOS hides it on app switch
&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSFloatingWindowLevel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setCollectionBehavior_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSWindowCollectionBehaviorCanJoinAllSpaces&lt;/span&gt;
                              &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;NSWindowCollectionBehaviorFullScreenAuxiliary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setSharingType_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSWindowSharingNone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The other three lines are there because a floating helper is useless if it disappears: it has to stay on top when the call app is in front, follow you across Spaces and sit over full-screen apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Trust me" wasn't good enough
&lt;/h2&gt;

&lt;p&gt;There are a lot of forum posts saying this flag works, and some saying it doesn't on newer macOS. I didn't want to tell people "it's hidden" based on a forum post, so I wrote a test that measures it.&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open two plain boxes on screen: a &lt;strong&gt;magenta&lt;/strong&gt; one set up exactly like Murmury's window, and a &lt;strong&gt;green&lt;/strong&gt; one that's an ordinary window.&lt;/li&gt;
&lt;li&gt;Capture the screen three different ways.&lt;/li&gt;
&lt;li&gt;Count the pixels of each color in every capture.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It passes only if the green box shows up in every capture (so the capture really worked) and the magenta box shows up in none.&lt;/p&gt;

&lt;p&gt;The three captures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ScreenCaptureKit&lt;/strong&gt;, which is what Zoom, Google Meet in Chrome and Teams use on current macOS. That needs a tiny Swift helper, because there's no handy Python binding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;screencapture&lt;/code&gt;&lt;/strong&gt;, the same thing as a normal macOS screenshot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CGWindowListCreateImage&lt;/code&gt;&lt;/strong&gt;, the older API some apps still use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Counting pixels is a few lines of numpy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RGB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the ScreenCaptureKit side, in Swift:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="kt"&gt;SCShareableContent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;excludingDesktopWindows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;onScreenWindowsOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;displays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SCContentFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;excludingWindows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SCStreamConfiguration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="kt"&gt;SCScreenshotManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;captureImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;contentFilter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the filter: &lt;code&gt;excludingWindows: []&lt;/code&gt;. I'm not asking ScreenCaptureKit to skip anything. If the window disappears, it's because of the window's own sharing type.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I measured
&lt;/h2&gt;

&lt;p&gt;On my MacBook (macOS 26.6.2, October 2026): &lt;strong&gt;0 pixels&lt;/strong&gt; of the hidden window in all three captures, while the ordinary window was captured every time (between 93k and 369k pixels, depending on the capture).&lt;/p&gt;

&lt;p&gt;Two things surprised me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ScreenCaptureKit still lists the window.&lt;/strong&gt; Its title and position show up in the list of shareable windows. It just isn't drawn in the image. So an app could know a hidden window is there, it can't see what's in it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;This can change with any macOS update.&lt;/strong&gt; That's why the test lives in the repo instead of in a blog post. After every update I run &lt;code&gt;uv run python scripts/check_hidden.py&lt;/code&gt; again, and anyone can run it on their own Mac.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the honest limits: this only covers software capture on your Mac. A phone pointed at your screen, an HDMI capture card or a second monitor that someone can see are a different story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Funny side effect
&lt;/h2&gt;

&lt;p&gt;When I wanted to record a demo GIF for the README, the recording came out with an empty space where the window should be. Of course. So the app has a testing flag, &lt;code&gt;--show-in-screenshots&lt;/code&gt;, that switches the sharing type back to read-only. The GIF in the README is recorded with that flag on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of the app
&lt;/h2&gt;

&lt;p&gt;If you're curious about the other parts: the call's audio comes from ScreenCaptureKit too (as 16 kHz float32 PCM from a small Swift helper), Whisper large-v3-turbo transcribes it on the Apple GPU with MLX, and each line goes in parallel to a quick "is this a question for me?" check and to the answer itself, so the answer starts right away and gets dropped if it wasn't a question. You bring your own key for Claude, OpenAI, Gemini, Grok, Groq, Mistral or OpenRouter.&lt;/p&gt;

&lt;p&gt;It's free and GPL-3.0: &lt;a href="https://github.com/GianlucaBave/murmury" rel="noopener noreferrer"&gt;github.com/GianlucaBave/murmury&lt;/a&gt;. If you run &lt;code&gt;check_hidden.py&lt;/code&gt; on your Mac, I'd love to know what it prints, especially on older macOS versions.&lt;/p&gt;

</description>
      <category>swift</category>
      <category>python</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
