DEV Community

orca forge
orca forge

Posted on Originally published at forge.workstyle.tech

Why headless Chromium Wheel Input Sends a 3D Page Back

📝 Originally published (in Japanese) at forge.workstyle.tech.

TL;DR The main thread can delay wheel events while 3D rendering is busy. A 160ms timer can expire first and move the page back when its position is below 0.5. Pausing render loops during page swaps and extending the wait to 220ms fixed the issue in the same test environment.

TL;DR

While 3D was rendering, the page sometimes moved backward when I used the mouse wheel. The main thread was blocked, so wheel events arrived late. I stopped the render loop while switching pages and increased the idle timeout used to decide that input had stopped from 160 ms to 220 ms. In the same test environment, one wheel turn now advances one page at a time while 3D is rendering. I also checked all 38 tests.

Why did scrolling the wheel make the page go back?

The cause was that wheel events arrived late when the main thread—the thread that drives page processing—was busy. The page transition uses a timer to decide when input has stopped.

When input arrives late, the 160 ms timer expires before the wheel event arrives. At that point, if the page position is less than 0.5, the page moves back to its original position. The time during which it looked like no input was arriving differed from the time when input had actually stopped. As a result, when 3D rendering kept the main thread busy, it also affected page transitions.

Why the presentation stayed on the first page even after loading

I first noticed the issue at 04:30 on October 11. In a headless Chromium test environment without a GPU, I was loading three 3D models onto the cover slide. While they took a few seconds to load, wheel input arrived late. If I scrolled during that time, the presentation sometimes failed to advance and returned to the first page.

When I removed the 3D models and tested again, all slides advanced correctly. I thought the issue was unlikely to occur on computers with a GPU, so I didn’t fix it at the time.

During a check immediately after the release on October 13, the presentation sometimes still wouldn’t advance from the first page when I scrolled in the same environment. Since the problem continued after the models had finished loading, temporary input delays during loading couldn’t explain it.

How I Stopped Rendering During Page Swaps

I decided to pause 3D rendering only while pages are being swapped. I add the deck-swapping class to the root element, and the 3D and other animation loops skip a frame when that class is present.

The relevant code looks like this:

root.classList.toggle('deck-swapping', Boolean(partner));   // While rendering the swap
...
if (!currentHost || document.hidden || root.classList.contains('deck-swapping') || ...) return;  // 3D draw
Enter fullscreen mode Exit fullscreen mode

In the same change, I increased the delay before bringing the pages together from 160ms to 220ms. I didn’t measure the effects of pausing rendering and extending the delay separately, so I don’t know how much each change contributed.

After the Fix, It Advanced One Page at a Time While Rendering 3D

I tried it again in the same test environment after the fix. Even with 3D rendering active, one scroll of the wheel advanced one page. The test suite had 38 tests.

What I confirmed is that the issue stopped occurring after the fix in this environment, and that each input triggered a single transition. I haven’t checked whether the same issue occurred before the fix on a physical machine with a GPU. I also haven’t measured how many milliseconds the input was delayed.

At first, I thought this was unlikely to happen on a computer with a GPU because I had only seen the issue in headless Chromium. But shortly after release, I found that the page still wouldn’t advance past page one after loading in the same test environment. My initial assumption that it was a loading issue wasn’t enough.

What I learned is that even when the page transition timer is waiting for input, a busy main thread can delay the input event itself. I paused 3D rendering only during the transition. At least in this test, that prevented wheel input during rendering from triggering unwanted transitions.

Key takeaways

  • Test wheel input both while 3D models load and after loading in headless Chromium without GPU use
  • Check whether removing 3D lets all slides advance correctly
  • Inspect whether the 160ms timer expires before the wheel event while the page position is below 0.5
  • Skip render-loop frames during page swaps and use a 220ms wait
  • Verify that one wheel input advances one page with 3D still rendering

Top comments (0)