Pet display flickers in Windows Terminal + WSL2

Open 💬 11 comments Opened May 29, 2026 by JustLookAtNow

What version of Codex CLI is running?

codex-cli 0.135.0

What subscription do you have?

team

Which model were you using?

_No response_

What platform is your computer?

windows x64 with wsl2

What terminal emulator and version are you using (if applicable)?

Windows Terminal (WSL)

Codex doctor report

What issue are you seeing?

Pet display flickers

What steps can reproduce the bug?

Open Windows Terminal.
Start a WSL2 shell.
Run Codex CLI.
Enable the pet display.
Observe the area where the pet is displayed.

What is the expected behavior?

The pet should render without flickering.

Additional information

_No response_

View original on GitHub ↗

11 Comments

JustLookAtNow · 1 month ago

<img width="2401" height="983" alt="Image" src="https://github.com/user-attachments/assets/52f8da9a-5381-44c9-96fe-20511650fb40" />
The GIF frame rate is a bit low, so some flickering went missing. Actually, any change in the image causes it to flicker

makoto-soracom · 1 month ago

I put together a small branch that may help with this flicker:

https://github.com/openai/codex/compare/main...makoto-soracom:makoto/pet-flicker-25004

The branch tries to reduce flicker by:

  • avoiding redundant ClearToEnd output on normal frame draws when the blank row tail is unchanged
  • preserving the ambient pet's Running animation start time when the running notification is refreshed
  • keeping the visible Working status indicator while suspending its self-scheduled 32ms animation frames during active task/composer interaction

Validation:

  • git diff --check
  • just test -p codex-tui
  • the new pet-flicker-related tests passed
  • the full local run still had unrelated environment/snapshot failures, including WSL-specific footer shortcut text, project/my-project resolving to tmp, and an existing active Exploring snapshot drift

I tested this in Windows Terminal + WSL2 and it appears to reduce the pet flicker in that environment.

I cannot open an upstream PR from this account because the repository currently limits PR creation to collaborators, so this branch is shared for maintainers/collaborators to test further or turn into a PR if useful.

JustLookAtNow · 1 month ago

@makoto-soracom it's works for me!good job!After switching to your branch, the flickering is almost completely gone.
However, I have discovered another new issue.
https://github.com/microsoft/terminal/issues/8389#issuecomment-4645091149

j4james · 1 month ago

@JustLookAtNow I'm not exactly sure what the issue is that you're referring to in that Windows Terminal comment, but if it's regarding the size difference between the kitty protocol and sixel, I think that's because the Pet renderer is using different height calculations for them.

Look at the constants here:
https://github.com/openai/codex/blob/9e0d7f02c9416c46dde6e571068a0fb03a4facdf/codex-rs/tui/src/pets/ambient.rs#L37-L39

And then look at the image_size calculation here:
https://github.com/openai/codex/blob/9e0d7f02c9416c46dde6e571068a0fb03a4facdf/codex-rs/tui/src/pets/ambient.rs#L334-L346

The result of this calculation is that the rows will always be 5 (75 / 15), and height_px will always be 75. The kitty renderer uses the rows field to determine what size the Pet will be displayed, so it will always be 5 rows high, but the sixel renderer uses the height_px field, so the number of rows occupied will vary from one terminal to the next. In Windows Terminal, the cell height is 20, so a height of 75 will cover 3.75 rows (75 / 20).

JustLookAtNow · 1 month ago

@j4james It's not about size or problem, but a noticeable difference in image quality - the actual smaller images displayed by Sixel are actually blurrier.

j4james · 1 month ago

@JustLookAtNow I see what you mean now. But that's just the result of the way the sixel colors have been generated. You can see what's happening here:

https://github.com/openai/codex/blob/a304569c796a0aceeb9221e4bd8daba0102d39a0/codex-rs/tui/src/pets/sixel.rs#L184-L189

Sixel is a palette-based image format, so you are typically limited to 256 colors. But the way to handle that is by finding the most commonly used colors in the image and then mapping the original image colors to that palette (using dithering if necessary). What is being done above is just dropping 16-bits worth of color resolution.

For a comparison of what it should look like, try running the original image through a sixel viewer like chafa or imgsixel.

makoto-soracom · 1 month ago

Update: I pushed a branch with the pet rendering fixes I tested:

What this branch changes:

  • adjusts Sixel pet rendering and clear area handling;
  • keeps transcript / scrollback reflow at the full terminal width while reserving pet space only for the composer;
  • suppresses the ambient pet redraw for the frame that flushes pending history, so scrollback text is less likely to be overwritten by the image draw;
  • adds regression coverage for the full-width reflow and composer reserve behavior.

I verified this in Windows Terminal + WSL2. In that setup, the branch reduces the pet flicker and preserves the expected transcript/composer behavior.

j4james · 1 month ago

One other thing I forgot to mention, if you're doing animation by clearing the area before drawing the next frame, you can avoid flickering by wrapping those two steps in a synchronized output block. All that requires is that you set the synchronized output mode (\x1b[?2026h) before performing the clear, and then reset it (\x1b[?2026l) after you've rendered the frame.

makoto-soracom · 1 month ago

I pushed an update to the flicker branch with @j4james's synchronized output suggestion applied around the Sixel clear/redraw path:

The change wraps the Sixel cell clear and the following Sixel frame write in CSI ? 2026 h / CSI ? 2026 l, and also wraps the clear-only path when the pet is hidden.

I verified this locally in Windows Terminal + WSL2, and the visible flicker is gone.

JustLookAtNow · 1 month ago
@JustLookAtNow I see what you mean now. But that's just the result of the way the sixel colors have been generated. You can see what's happening here:我现在明白你的意思了。但这只是六边形颜色生成方式造成的结果。你可以看看这里发生了什么: codex/codex-rs/tui/src/pets/sixel.rs Lines 184 to 189 in [a304569](/openai/codex/commit/a304569c796a0aceeb9221e4bd8daba0102d39a0) fn rgb332_index(red: u8, green: u8, blue: u8) -> u8 { let red = red >> 5; let green = green >> 5; let blue = blue >> 6; (red << 5) | (green << 2) | blue } Sixel is a palette-based image format, so you are typically limited to 256 colors. But the way to handle that is by finding the most commonly used colors in the image and then mapping the original image colors to that palette (using dithering if necessary). What is being done above is just dropping 16-bits worth of color resolution.Sixel 是一种基于调色板的图像格式,因此通常仅限于 256 种颜色。但解决办法是找到图像中最常用的颜色,然后将原始图像颜色映射到该调色板(必要时使用抖动)。上述操作实际上损失了 16 位颜色分辨率。 For a comparison of what it should look like, try running the original image through a sixel viewer like chafa or imgsixel.为了比较它应该是什么样子,可以尝试使用像 chafa 或 imgsixel 这样的六像素查看器来查看原始图像。

From what I understand, this is just a loss in color accuracy, but in reality the image becomes blurry and the resolution is also reduced

j4james · 1 month ago
the image becomes blurry and the resolution is also reduced

I explained this before in my earlier comment. The sixel version of the code is deliberately rendering the image at a smaller size (3.5 rows vs 5 rows for the kitty version). A smaller size means fewer pixels, and thus a lower resolution.