Problem
Shared screens (mostly slides/documents, i.e. text-heavy static content) arrive soft/blurry on subscribers, then sharpen after a few seconds. The worst of it is right at the start of a share. Text is the main casualty — it’s legible after it settles, but not during the ramp.
Environment
livekit-client2.16.0@livekit/components-react2.9.16livekit-server-sdk2.14.2- Next.js 15.2.6 / React 18.3.1
- Deployment: (Cloud / self-hosted + server version — fill in)
- Sharers: (browser + OS — fill in)
Current config (screen-share options intentionally left at SDK defaults)
const publishDefaults: TrackPublishDefaults = {
dtx: true,
videoSimulcastLayers: hq
? [VideoPresets.h540, VideoPresets.h360]
: [VideoPresets.h216, VideoPresets.h90],
red: !e2eeEnabled,
videoCodec, // 'vp8' by default
// screenShareEncoding / screenShareSimulcastLayers / degradationPreference
// are NOT set — relying on SDK defaults
};
return {
videoCaptureDefaults,
publishDefaults,
adaptiveStream: true,
dynacast: true,
singlePeerConnection: true,
};
Receive side, we already try to pin the focused share to the top layer:
// runs when the focused track changes or becomes subscribed
publication.setVideoQuality(VideoQuality.HIGH);
What I’ve already worked out from the SDK source
- Screen share already gets
maintain-resolutionfor free:
opts.degradationPreference ??= getDefaultDegradationPreference(track);
// returns 'maintain-resolution' for Track.Source.ScreenShare
Because of the ??=, setting degradationPreference in publishDefaults overrides this per-source logic for both camera and screen share. I tried 'maintain-framerate' and it made things clearly worse (it trades resolution for fps — exactly wrong for text). So I’ve left it unset.
-
Default
screenShareEncodingisScreenSharePresets.h1080fps15.encoding→ 1920×1080, 2.5 Mbps, 15 fps. -
When I set
screenShareSimulcastLayers: [h360fps15, h1080fps15], the ladder builder hits:
if (size >= 960 && midPreset) {
return encodingsFromPresets(width, height, [lowPreset, midPreset, original], ...);
}
i.e. three encodings (360p + 1080p + original). That seemed to make the startup ramp worse (bitrate split three ways) and left a 360p floor that adaptiveStream can park a subscriber on.
Questions
-
Startup ramp: is there a supported way to make a new screen-share track reach its target bitrate faster? The first seconds look like BWE probing upward while
maintain-resolutionholds full resolution at high QP. Is there a recommendedstartBitrate/ probing approach, or is this simply expected WebRTC behaviour? -
Simulcast for screen share: for text-heavy content, is a single layer (no simulcast for screenshare) the recommended setup, or is there a preferred ladder that avoids a low floor? If single-layer is preferred, what’s the clean way to disable simulcast only for screen share (
publishDefaults.simulcastlooks global)? -
adaptiveStream vs
setVideoQuality: doesadaptiveStreamre-evaluate and override a manualsetVideoQuality(HIGH)after the element is measured? Our tile has no measured size on first paint, so I suspect it starts on the lowest layer and gets promoted late. Is there a supported way to exempt a specific publication from adaptive-stream quality management? -
Codec: for screen share specifically, is VP9/AV1 meaningfully better here? I noticed the SDK forces
contentHint = 'motion'for screenshare with SVC codecs (the L1T3 workaround), and the comment says that overrides degradation preference tobalanced— does that hurt text sharpness compared to staying on VP8? -
Is setting
contentHintto'detail'/'text'on the screen-share track recommended/supported here, and does it conflict with the SVC workaround above?
What is the proper solution for this issue?