Common Screen Sharing Bugs and How to Catch Them

Common Screen Sharing Bugs and How to Catch Them is a practical guide for engineers who need to spot and fix issues that appear only when users share their screens. Screen sharing adds a layer of comp

June 16, 2026 · 18 min read · Common Issues

Common Screen Sharing Bugs and How to Catch Them is a practical guide for engineers who need to spot and fix issues that appear only when users share their screens. Screen sharing adds a layer of complexity that sits between the application UI, the operating system’s capture pipeline, and the remote viewer. Bugs that are invisible in normal use can surface as black screens, misaligned cursors, or missing controls, leading to frustrated users and failed workflows. This article walks through the most common patterns, explains why they happen, shows how to reproduce them, and gives concrete fixes and prevention steps. You will find a test matrix, manual and automated detection techniques, real‑world examples, and a short checklist you can bookmark for future releases.

Common Screen Sharing Bugs and How to Catch Them: Overview

What screen sharing entails

When a user initiates a share, the operating system grabs a bitmap of the target window or desktop, encodes it, and sends it over a network channel. On the receiver side, the stream is decoded and rendered in a view that may apply scaling, cropping, or overlay UI. The sharing process typically involves three components: the capture source (e.g., a desktop duplication API on Windows, ScreenCapture on macOS, or MediaProjection on Android), the encoding/transport layer (often WebRTC, RTMP, or a proprietary codec), and the remote display client. Each component can introduce timing, permission, or format mismatches that only manifest when the pipeline is active.

Why bugs appear only in sharing contexts

Standard functional tests exercise the UI directly, bypassing the capture and encode steps. Consequently, they miss issues that depend on:

Understanding these factors helps you design tests that replicate the sharing pipeline rather than just the UI.

Common Screen Sharing Bugs and How to Catch Them: Classification of Bugs

Visual rendering issues

These bugs affect what the remote viewer sees. Common symptoms include black or blank screens, incorrect scaling, color shifts, or missing UI overlays. They often arise when the capture source receives a surface that is not yet fully rendered, or when the encoder expects a specific pixel format (e.g., NV12) but the source provides RGBA.

Input forwarding problems

When the remote user interacts with the shared view (e.g., clicking a button), the coordinates must be mapped back to the source window. Errors in this mapping produce cursor offsets, clicks that hit the wrong element, or drag operations that stop prematurely. Causes include mismatched DPI settings, hidden title bars, or dynamic window resizing during the share.

Permission and security glitches

Screen sharing is a privileged operation. On macOS, the user must grant Screen Recording permission; on Windows, the app may need the “Capture isolated window” capability; on Android, MediaProjection requires a user‑granted intent. If the app does not handle denial gracefully, it may enter a loop of permission prompts, or the share may start but immediately terminate when a protected surface (e.g., DRM‑protected video) is encountered.

Performance and latency anomalies

The capture‑encode‑decode pipeline adds latency. If the app performs heavy work on the UI thread during a share, frame drops become visible as stutter or frozen video. Conversely, overly aggressive encoding can spike CPU usage, causing thermal throttling on mobile devices and resulting in a degraded experience for both the sharer and the viewer.

Session persistence bugs

A share may survive app backgrounding, orientation changes, or network interruptions, but many implementations fail to pause and resume correctly. Symptoms include the share stopping when the app goes to the background, failing to recover after a network glitch, or leaving a “zombie” capture session that consumes resources until the device is rebooted.

Common Screen Sharing Bugs and How to Catch Them: Detection Strategies (Manual)

Exploratory testing with personas

Scripted test cases follow predetermined paths, but real users share screens in unpredictable ways. By employing personas—curious, impatient, novice, adversarial, elderly, accessibility‑focused, and power‑user—you can uncover edge cases that scripts miss. For example, an “elderly” persona might increase system font size, triggering layout shifts that break coordinate mapping. An “adversarial” persona might repeatedly deny permissions, exposing poor error handling.

To run a persona‑driven session manually:

  1. Launch the app on a test device or VM.
  2. Activate the desired persona profile (e.g., set font scale to 200 %, enable high‑contrast mode, or use a screen reader).
  3. Initiate a share to a secondary viewer (another device, a screen‑recording tool, or a remote desktop client).
  4. Perform typical tasks (login, form fill, drag‑and‑drop) while observing the remote view for visual or interaction anomalies.
  5. Note any deviation from expected behavior and capture logs from both the sharing SDK and the OS.

Using screen recording and diff tools

A quick way to spot rendering differences is to record the local screen (what the user sees) and the remote share simultaneously, then compare frames. Tools like ffmpeg can grab the desktop, while compare from ImageMagick computes pixel differences.


# Record local screen (X11) at 30 fps
ffmpeg -video_size 1920x1080 -framerate 30 -f x11grab -i :0.0 local.mkv

# In another terminal, record the remote view (assuming it appears in a window titled "Viewer")
ffmpeg -video_size 1280x720 -framerate 30 -f x11grab -i $(xdotool search --name "Viewer" getwindowgeometry --shell | grep -oP '0x\w+') remote.mkv

# Convert to image sequences for diff
ffmpeg -i local.mkv -vf fps=30 local_%04d.png
ffmpeg -i remote.mkv -vf fps=30 remote_%04d.png

# Compute mean squared error per frame
for i in $(printf "%04d" {0001..0300}); do
  compare -metric MSE local_$i.png remote_$i.png null: 2>> diff.log
done

A steadily rising MSE indicates drift, while spikes point to occasional dropped frames or resolution changes.

Checklist for manual testers

AreaItemWhy it matters
VisualVerify that the remote view shows all UI elements at correct size and colorDetects clipping, scaling, or color‑space bugs
InputClick each visible control and confirm the action matches the local viewChecks coordinate mapping and DPI handling
PermissionsTry sharing after denying, then granting permission; observe app reactionEnsures graceful handling of consent flows
PerformanceMonitor CPU/GPU usage (e.g., via top or Activity Monitor) while sharingReveals resource‑hungry encoding or UI work
PersistenceBackground the app, return after 10 s; confirm share resumesTests session pause/resume logic
NetworkSimulate packet loss with tc or Network Link Conditioner; watch for freezesValidates resilience to jitter and bandwidth drops
AccessibilityEnable a screen reader; verify announcements are forwarded to the remote viewerConfirms that accessibility tree is included in the capture

Running through this checklist for each release candidate catches the majority of sharing‑specific regressions before they reach users.

Common Screen Sharing Bugs and How to Catch Them: Detection Strategies (Automated)

Instrumenting the sharing SDK

Most platforms expose callbacks when a share starts, stops, or encounters an error. By wrapping these callbacks with logging and assertions, you can turn a manual exploratory step into an automated check.

Android (MediaProjection)


MediaProjectionCallback callback = new MediaProjectionCallback() {
    @Override
    public void onStop() {
        Log.d("Share", "Projection stopped unexpectedly");
        // Fail the test if stop occurs without user action
        Assert.fail("MediaProjection stopped during test");
    }
};

mediaProjection.registerCallback(callback, handler);

iOS (ReplayKit)


RPScreenRecorder.shared().isMicrophoneEnabled = false
RPScreenRecorder.shared().startCapture { (sampleBuffer, sampleType, error) in
    if let error = error {
        print("Capture error: \(error)")
        XCTFail("Capture failed: \(error.localizedDescription)")
    }
    // process frame …
}

Web (getDisplayMedia)


let stream;
try {
    stream = await navigator.mediaDevices.getDisplayMedia({video: true});
} catch (e) {
    console.error("Display media denied:", e);
    throw new Error("Unable to start share");
}
stream.getVideoTracks()[0].addEventListener('ended', () => {
    console.warn("Share ended unexpectedly");
    // trigger test failure
});

These snippets can be integrated into UI test frameworks (Espresso, XCUITest, Playwright) to assert that the share remains active for the duration of a scenario.

Visual regression with pixel comparison

Automated visual testing shines when you need to verify that the remote view matches a baseline despite dynamic content. Tools like Applitools, Percy, or open‑source pixelmatch can compare a captured frame from the remote viewer against a reference image.

Playwright example


const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://example.com/app');

  // Start share via a button that triggers getDisplayMedia
  await page.click('#start-share');

  // Wait for the remote viewer iframe to appear
  const viewer = await page.waitForSelector('#remote-viewer iframe');
  const frame = await viewer.contentFrame();

  // Capture a screenshot of the remote view
  const screenshot = await frame.screenshot({ path: 'remote.png' });

  // Compare with baseline using pixelmatch (node module)
  const { pixelmatch } = require('pixelmatch');
  const PNG = require('pngjs').PNG;
  const img1 = PNG.sync.read(require('fs').readFileSync('remote-baseline.png'));
  const img2 = PNG.sync.read(require('fs').readFileSync('screenshot.png'));
  const diff = new PNG({ width: img1.width, height: img1.height });
  const mismatched = pixelmatch(img1.data, img2.data, diff.data, img1.width, img1.height, {threshold: 0.1});
  require('fs').writeFileSync('diff.png', PNG.sync.write(diff));

  if (mismatched > 0) {
    throw new Error(`Visual regression: ${mismatched} mismatched pixels`);
  }

  await browser.close();
})();

If the mismatch count exceeds a tolerant threshold (often zero for static UI, a few pixels for anti‑aliased text), the test fails, prompting investigation.

Load and stress testing for sharing sessions

Sharing can exacerbate resource contention under load. Automated scripts that launch many concurrent shares help surface throttling, memory leaks, or encoder crashes.

k6 script for WebRTC‑based sharing


import http from 'k6/http';
import { sleep, check } from 'k6';

export const options = {
  vus: 20,          // 20 virtual users sharing simultaneously
  duration: '5m',
};

export default function () {
  const params = {
    headers: {
      'Content-Type': 'application/json',
    },
  };
  const payload = JSON.stringify({ sessionId: __VU });

  const res = http.post('https://api.example.com/start-share', payload, params);
  check(res, {
    'share started': (r) => r.status === 200,
  });

  // Simulate user interaction during share
  sleep(1);
  http.get('https://api.example.com/ping');

  // Stop share after random interval
  if (Math.random() < 0.2) {
    const stop = http.post('https://api.example.com/stop-share', payload, params);
    check(stop, { 'share stopped': (r) => r.status === 200 });
  }

  sleep(2);
}

Running this script while monitoring server CPU, bandwidth, and client‑side metrics reveals whether the sharing infrastructure scales.

Using SUSA for autonomous exploration (mention SUSA)

SUSA’s autonomous agent can be pointed at an APK or a web URL and will exercise the app with a variety of personas while automatically detecting screen‑sharing anomalies. Because the agent explores without pre‑written scripts, it stumbles upon states that manual testers might overlook, such as a share initiated from a deep‑link screen or a share that occurs while a modal dialog is open.

To invoke SUSA from the command line:


pip install susatest-agent
susatest explore --apk ./app-release.apk --personas all --output ./susareport.json

The resulting report includes a “Screen Sharing” section that lists any black‑screen events, permission loops, or input‑offset anomalies discovered during the autonomous run. Integrating this step into a CI pipeline gives you continuous feedback on sharing health without maintaining a growing suite of scripted tests.

Common Screen Sharing Bugs and How to Catch Them: Bug Patterns and Fixes

Below are ten recurring patterns, each described with symptom, root cause, reproduction steps, detection approach, and remediation.

Bug 1: Black screen on share start

Symptom – The remote viewer sees a completely black frame, while the local UI is visible.

Cause – The capture source receives a surface before the UI layer has finished drawing, or the encoder expects a pixel format that the surface does not provide (e.g., providing RGBA when the encoder only accepts NV12).

Reproduction – Launch the app, immediately trigger a share (e.g., via a floating action button) before the first frame is rendered. On Android, use adb shell am start -n com.example/.MainActivity then immediately send a keyevent to open the share UI.

Detection – Manual: look for black remote view. Automated: assert that the first frame captured after share start contains non‑zero pixel variance (e.g., compute average luminance > 5).

Fix – Defer the share request until after the first UI frame is drawn. On Android, use ViewTreeObserver.OnDrawListener; on iOS, wait for viewDidAppear; on the web, listen for pageshow after a Promise.resolve().then(() => startShare()). Ensure the surface format matches encoder expectations by explicitly setting the pixel format when creating the MediaProjection or capture session.

Bug 2: Cursor offset/misalignment

Symptom – Clicks in the remote view land a few pixels away from the intended target; dragging appears to drift.

Cause – Mismatch between DPI scaling of the source window and the coordinates reported by the sharing pipeline. Common on multi‑monitor setups with different scale factors, or when the remote viewer applies its own scaling.

Reproduction – Set the primary display to 150 % scaling, open a window on a second monitor at 100 %, start a share, and click a button near the window edge. Observe the offset.

Detection – Manual: use a visual overlay that shows the expected click location (e.g., a small red dot) and compare to the actual remote cursor. Automated: inject a JavaScript snippet that dispatches a click at known coordinates and asserts that the resulting DOM event reports the same coordinates after adjusting for the known scale factor.

Fix – Query the source window’s device pixel ratio (window.devicePixelRatio on the web, GetDpiForWindow on Windows, UIScreen.main.scale on iOS) and apply the same factor when remapping input events. If the remote viewer applies additional scaling, negotiate the scale factor during the signaling exchange (e.g., include DPI in the offer/answer SDP).

Bug 3: Missing UI elements (overlay not captured)

Symptom – Certain overlays, such as toast notifications or custom drawn views, do not appear in the shared stream.

Cause – The overlay is rendered to a layer that is excluded from the capture target (e.g., a secure surface, a hardware overlay, or a view with setSecure(true) on Android).

Reproduction – Trigger a toast while sharing; verify that the toast appears locally but not remotely. On Android, call window.getDecorView().setSecure(true) before sharing to see the effect.

Detection – Manual: visually confirm missing overlay. Automated: compute a perceptual hash (e.g., using phash) of frames before and after overlay appearance; a significant change indicates the overlay was captured.

Fix – Ensure that any UI that must be visible in a share is rendered to a non‑secure surface. If security is required (e.g., DRM content), consider compositing the secure content onto a non‑secure layer after encryption, or use platform‑provided APIs that allow secure content to be included in a share (e.g., Android’s FLAG_SECURE exemptions for certain media projection use cases).

Bug 4: Audio/video desync

Symptom – The remote viewer hears audio that is ahead of or behind the video, leading to lip‑sync issues.

Cause – Independent pipelines for audio and video with different buffering strategies, or the sharing app pausing video capture while continuing audio capture (or vice versa) during temporary CPU spikes.

Reproduction – Play a video with a distinct audio cue (e.g., a clap) while sharing. Use a stopwatch to measure the delay between seeing the clap and hearing it.

Detection – Manual: observe lip sync. Automated: inject a known audio tone and a flashing visual marker at the same timestamp, then compute cross‑correlation between the audio envelope and the visual intensity over time. A lag > 40 ms is perceptible.

Fix – Use a synchronized clock (e.g., RTP timestamps) for both streams, and ensure the encoder receives audio and video frames from the same timebase. If using WebRTC, rely on its built‑in synchronization; if building a custom pipeline, use a single media foundation transform that interleaves audio and video packets.

Bug 5: Permission prompts looping

Symptom – After denying screen‑recording permission, the app immediately shows the permission dialog again, trapping the user.

Cause – The app treats a denial as a transient error and retries the share request without backing off or informing the user.

Reproduction – Deny the permission prompt when the OS asks for screen recording, then attempt to share again via the UI.

Detection – Manual: notice the repeated dialog. Automated: count the number of permission dialogs shown within a short time window (e.g., 5 seconds) using UI automation tools that can detect system alerts.

Fix – On denial, disable the share button, show an inline explanation, and only re‑enable after the user navigates away and returns or explicitly taps a “Try again” button. Respect the OS‑provided “don’t ask again” flag by checking the persisted permission state before requesting.

Bug 6: Share stops when app goes to background

Symptom – The remote view freezes or goes black as soon as the sharing app is backgrounded (e.g., user switches to another app).

Cause – The capture session is tied to the foreground window or activity lifecycle; when the app loses focus, the OS revokes the capture grant.

Reproduction – Start a share, then press the home button or switch to another app. Observe the remote view.

Detection – Manual: see the freeze. Automated: monitor the sharing callback for a onStop event correlated with onPause/onStop lifecycle events.

Fix – If the use case permits, request a capture that survives backgrounding (e.g., Android’s MediaProjection can continue if the app holds a foreground service; on iOS, use ReplayKit with isBroadcast set to true for background sharing). Clearly inform the user that sharing will stop when the app is backgrounded if background capture is not supported.

Bug 7: Excessive CPU/memory leading to throttling

Symptom – The device becomes hot, frame rate drops, or the share appears choppy after a few minutes.

Cause – The encoder is configured with a high bitrate or resolution, or the UI thread is performing expensive work (e.g., heavy animations) while the capture runs.

Reproduction – Start a share, enable CPU‑intensive background work (e.g., a cryptographic hash loop), and monitor temperature/frame rate via adb shell dumpsys cpuinfo or Instruments.

Detection – Manual: observe choppy video and device heating. Automated: collect encoder FPS and CPU usage metrics; assert that FPS stays above a threshold (e.g., 24 fps) and CPU stays below a safe limit (e.g., 70 % of a core).

Fix – Adaptive bitrate: lower resolution or frame rate when device temperature exceeds a threshold. Offload non‑UI work to background threads or use RenderScript/Metal compute shaders. On the web, use the navigator.getBattery() API to reduce quality when battery is low.

Bug 8: Inability to share specific windows (security sandbox)

Symptom – The share picker lists the app’s window, but selecting it results in a black screen or an error.

Cause – The window is marked as secure or belongs to a different user session (e.g., a sandboxed container, a virtual desktop, or a window with the WS_EX_NOREDIRECTIONBIT flag).

Reproduction – Launch the app inside a Windows Sandbox or an Android work profile, then attempt to share its main window.

Detection – Manual: verify that the picker shows the window but the remote view is black. Automated: after selecting a window, check for a capture error callback or a black‑frame detection as in Bug 1.

Fix – Document the limitation and provide a fallback: share the entire screen or desktop instead of the specific window. If the app must share a particular surface, request the user to move the content to a non‑secure container (e.g., drag a video out of a DRM‑protected player).

Bug 9: Session resume after network glitch fails

Symptom – After a brief network interruption, the share does not recover; the remote view stays frozen.

Cause – The signaling channel does not attempt to renegotiate or the encoder does not recover from a broken packet stream.

Reproduction – Start a share, then use tc to add 50 % packet loss for 10 seconds, then restore connectivity. Observe whether the share resumes.

Detection – Manual: see if the video resumes after the loss period. Automated: monitor the RTCP feedback messages; assert that a PLI (Picture Loss Indication) is sent and that a keyframe is received within a configurable timeout (e.g., 2 seconds).

Fix – Implement robust retransmission and keyframe request logic. For WebRTC, rely on the built‑in congestion control; for custom pipelines, send periodic keyframe requests and buffer enough frames to survive a short loss burst.

Bug 10: Accessibility announcements not forwarded

Symptom – A screen reader user hears local announcements but the remote viewer (who may also rely on assistive tech) does not receive them.

Cause – The accessibility tree is not included in the captured video stream, or the app does not forward accessibility events to the sharing pipeline.

Reproduction – Enable TalkBack (Android) or VoiceOver (iOS), perform an action that triggers a toast, and verify whether the remote viewer’s screen reader reads the toast.

Detection – Manual: listen for spoken feedback on the remote side. Automated: on Android, use AccessibilityService to capture events and assert that they are relayed via a custom broadcast that the sharing agent logs.

Fix – When possible, render accessibility-relevant information as visual cues that will be captured (e.g., overlay a live‑region badge). Alternatively, send accessibility events through a separate data channel (e.g., WebRTC data channels) and have the remote client synthesize speech locally.

Common Screen Sharing Bugs and How to Catch Them: Test Matrix

Bug IDSymptomReproduction StepsDetection (Manual)Detection (Automated)Primary Fix
B1Black screen on share startTrigger share immediately after app launch, before first frameLook for black remote viewAssert first frame luminance > 5Defer share until UI drawn; match pixel format
B2Cursor offset/misalignmentSet mixed DPI displays, click near window edgeVisual overlay shows mismatchInject click at known coords, verify reported coordsApply source DPI to input mapping; negotiate scale
B3Missing UI elements (overlay not captured)Show toast while sharingToast absent remotelyCompare frame hash pre/post overlayRender overlays to non‑secure surface or use exempt API
B4Audio/video desyncPlay video with clap, measure lagLip‑sync noticeably offCross‑correlate audio tone & visual flashUse synchronized RTP timestamps; single media foundation
B5Permission prompts loopingDeny permission, retry shareDialog repeatsCount system alerts in short windowDisable button on denial, show explanation, respect “don’t ask again”
B6Share stops when app goes to backgroundBackground app during shareRemote view freezesCorrelate onStop with lifecycle callbacksRequest foreground service or background‑capable capture
B7Excessive CPU/memory → throttlingShare + CPU‑intensive loop, monitor tempChoppy video, device hotAssert FPS > 24, CPU < 70 % coreAdaptive bitrate, offload work, battery‑aware quality
B8Inability to share specific windows (sandbox)Share window inside Windows Sandbox/Android work profilePicker shows window, remote blackDetect capture error or black frame after selectionFallback to full‑screen/share; advise moving content out of sandbox
B9Session resume after network glitch failsInduce packet loss, then restoreShare stays frozenMonitor RTCP PLI & keyframe receipt timeoutImplement retransmission, periodic keyframe request
B10Accessibility announcements not forwardedEnable TalkBack/VoiceOver, trigger toastRemote screen reader silentLog accessibility events, verify forwardingRender accessibility info visually or forward via data channel

This matrix gives you a quick reference for building test cases, assigning owners, and verifying fixes.

Common Screen Sharing Bugs and How to Catch Them: Prevention Checklist

Design considerations

CI/CD integration

  1. Unit‑level contracts – Write tests for the sharing wrapper that assert correct callbacks (start, stop, error) are invoked under simulated permission grants and denials.
  2. Visual regression snapshots – After each UI change, run the Playwright script from the “Visual regression with pixel comparison” section and store the baseline in an artifact repository. Fail the build on any mismatch beyond the allowed threshold.
  3. Load test gate – Execute the k6 load script against a staging environment as part of the nightly pipeline; enforce thresholds on average FPS and error rate

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free