Black border in the right side

Hi. I’m having a small issue with egress. When I’m streaming live, viewers can see a black border on the right side of the live stream. Do you have any idea how I could fix this issue? Thank you!

Screenshot 2026-06-05 at 15.27.38

This is almost certainly an aspect ratio or viewport mismatch in your egress configuration.

When you use a custom template (custom_base_url), the egress renderer may launch Chrome at 1279×719 instead of your intended resolution. That 1px gap on the right is exactly what happens in this case the canvas ends up slightly smaller than the output frame, which produces the visible black edge.

This has been reported before and is currently not being actively addressed upstream, so it’s unlikely to be patched on the LiveKit side.

Two reliable fixes:

  1. Use one of LiveKit’s built-in layouts (grid or speaker) instead of a custom template. These correctly inherit viewport sizing from the encoding preset and don’t hit this issue.
  2. If you need a custom template, avoid using EncodingOptionsPreset. Instead, define explicit encoding options and set exact width and height values. This bypasses the hardcoded 1279×719 fallback and forces correct rendering dimensions.

Can you confirm whether you’re using custom_base_url in your StartRoomCompositeEgress call? That will help identify which path is causing it. RoomComposite & web egress | LiveKit Documentation

@ioan, Most common cause of a black border on one side of egress output is aspect ratio mismatch between source video and egress canvas. Default RoomComposite / Web egress output is 1920×1080 landscape [Egress API | LiveKit Documentation]. If your source is portrait (mobile capture, 9:16) or any aspect narrower than 16:9, the canvas gets filled with the source plus black bars to make up the difference.

If your source is portrait, switch to a portrait preset on the egress request:

  from livekit import api

  req = api.RoomCompositeEgressRequest(
      room_name="my-room",
      layout="speaker",
      preset=api.EncodingOptionsPreset.PORTRAIT_H264_720P_30,
      segment_outputs=[...],
  )
  res = await lkapi.egress.start_room_composite_egress(req)

That uses 720×1280 instead of the default 1920×1080 [ Egress examples | LiveKit Documentation ]. Other portrait variants exist at 1080P and 60 fps in the same enum [ Egress API | LiveKit Documentation ].

Two quick checks before changing config:

  • What’s your egress type? Track egress takes source aspect directly. Composite / Web egress uses the canvas you configured.
  • Is the bar centered on both sides, or only the right? Only on one side usually points at a custom layout template positioning the video off-center rather than a pure aspect mismatch.

If you need fully custom canvas sizing (non-preset), EncodingOptions(width=..., height=..., framerate=..., video_bitrate=...) on the request gives explicit control. Fit/fill behavior beyond the canvas crop landed via livekit/egress#74 (closed 2023).

Hello, guys.

Thank you for your responses. I really appreciate it! I’ve tried all the suggested approaches, but I still can’t figure out what I’m doing wrong.

This is my version of the code:

const isMobile = ls.roomLayout.previewType === LSMultihostPreviewType.Mobile;
encodingOptions = new EncodingOptions({
    width: isMobile ? (isMulticast ? 720 : 1080) : isMulticast ? 1280 : 1920,
    height: isMobile ? (isMulticast ? 1280 : 1920) : isMulticast ? 720 : 1080,
    framerate: 30,
    videoBitrate: isMulticast ? 2500 : 4000, // 2.5 Mbps at 720p, 4 Mbps at 1080p
    keyFrameInterval: 2,
    videoCodec: VideoCodec.H264_MAIN,

    audioCodec: AudioCodec.AAC,
    audioBitrate: 64,
    audioFrequency: 44100,
});

const info = await this.egressClient.startRoomCompositeEgress(
    roomName, {
        stream: streamOutput
    }, {
        layout: 'speaker', // "grid", "single-participant"
        customBaseUrl: layoutUrl,
        // encodingOptions: EncodingOptionsPreset.PORTRAIT_H264_1080P_30,
        encodingOptions,
    },
);

To me, it does not look like an aspect ratio mismatch.

Fastest way to confirm it’s the template and not egress: open your template URL in a normal Chrome window sized to exactly 720×1280, or run:

lk egress test-template --base-url <your-template-url> --room <room> --layout speaker --publishers 2

You’ll see the same right-side strip and can fix it live in devtools before re-running egress.

Two quick checks that tell what maybe the iisue:

  • Does the strip disappear if you switch from your customBaseUrl template to the built-in speaker layout (leave customBaseUrl unset)? If yes, it’s purely your template CSS.
  • Is the strip a fixed pixel width (~15px → scrollbar gutter) or proportional to the frame (→ breakpoint / object-fit)?

Make the root fill the compositor and clip everything else:

html, body, #root {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #000;
}
/* your video stage */
.stage {
  position: fixed;
  inset: 0;           /* fills the full 720x1280 */
  width: 100vw;
  height: 100vh;
}
.stage video {
  width: 100%;
  height: 100%;
  object-fit: cover;  /* use 'contain' if you'd rather show the whole source with intentional bars */
}

Some helpful docs:

  1. Custom recording templates: Custom recording templates | LiveKit Documentation
  2. Portrait recording requirements (min 500px width, must stay portrait at ≥720px): Egress examples | LiveKit Documentation