From 08f1f17b1d6e83301bc7c764ff3b869cabbefd9d Mon Sep 17 00:00:00 2001 From: Minitor <99525672+MinitorMHS@users.noreply.github.com> Date: Sun, 27 Sep 2026 22:30:51 +0000 Subject: [PATCH 1/2] Request display audio so the picker offers a share-audio checkbox getScreenshare({ audio: false }) passes audio: undefined to getDisplayMedia, so Chrome/Edge never render the "Share tab audio" checkbox and tab/system audio cannot be shared at all. Flip to audio: true so the constraint is actually requested. Picker UI only: nothing subscribes to screenshare.audio.broadcastTrack$, so no extra transceiver is created and the E2EE media path is untouched. This is deliberately isolated from the earlier attempt (486be4e), which coupled the same flag to pushing a second audio track through PartyTracks. --- app/hooks/useUserMedia.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/hooks/useUserMedia.ts b/app/hooks/useUserMedia.ts index 1fd43960..ec582476 100644 --- a/app/hooks/useUserMedia.ts +++ b/app/hooks/useUserMedia.ts @@ -24,7 +24,12 @@ export const camera = getCamera({ broadcasting: true, constraints: { width: { ideal: 1280 }, height: { ideal: 720 } }, }) -export const screenshare = getScreenshare({ audio: false }) +// audio: true → pass an audio constraint to getDisplayMedia so Chrome/Edge +// render the "Share tab audio" checkbox in the picker. This only affects +// the picker UI; the resulting track is NOT pushed through PartyTracks +// (nothing subscribes to screenshare.audio.broadcastTrack$), so no +// additional encrypted stream is created and E2EE is unaffected. +export const screenshare = getScreenshare({ audio: true }) function useNoiseSuppression() { const [suppressNoise, setSuppressNoise] = useLocalStorage( From ebd240f968b363f1e4e8529f3324a3a6a5d89c14 Mon Sep 17 00:00:00 2001 From: Minitor <99525672+MinitorMHS@users.noreply.github.com> Date: Sun, 27 Sep 2026 23:54:18 +0000 Subject: [PATCH 2/2] Show your own screen share locally For self + screenshare the tile read from pulledVideoTrack, but Cloudflare Calls never loops a track back to its sender, so that is always undefined and the tile renders empty. The other participants see the screen; the sharer sees nothing. Use the local capture track for the self screenshare tile, matching how the self webcam tile already uses userMedia.videoStreamTrack. Rendering only - no track is pushed, pulled, or re-encrypted, so the E2EE media path is unchanged. --- app/components/Participant.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/components/Participant.tsx b/app/components/Participant.tsx index 65a3eb98..61cb51ec 100644 --- a/app/components/Participant.tsx +++ b/app/components/Participant.tsx @@ -95,8 +95,16 @@ export const Participant = forwardRef< preferredRid ) const audioTrack = isSelf ? userMedia.audioStreamTrack : pulledAudioTrack + // For your own screenshare there is no server echo to pull (Cloudflare + // Calls doesn't loop a track back to its sender), so pulledVideoTrack is + // always undefined and the tile renders empty. Use the local capture + // track for the self screenshare instead. const videoTrack = - isSelf && !isScreenShare ? userMedia.videoStreamTrack : pulledVideoTrack + isSelf && !isScreenShare + ? userMedia.videoStreamTrack + : isSelf && isScreenShare + ? userMedia.screenShareVideoTrack + : pulledVideoTrack useDeadPulledTrackMonitor( user.tracks.video,