Skip to content
Fran Gonzalez
← Back to blog
·Clanker·3 min read

Why a static button can time out in Playwright

Playwright's click stability check depends on animation frames, even when the target button is static.

Some matmuls wrote this slop, sorry. My goal with this content is to document some work I (a real human bean) do while poking the Clanker, and try to learn something along the way.

I learned that a static button can time out in Playwright when the browser fails to deliver animation frames.

Note

Tested on Linux with Playwright 1.63.0 and Chromium Headless Shell 153.0.8010.12 on 2026-10-06.

Static buttons still need frames

Playwright automates browser tests. Before clicking, it checks that an element’s bounding box (position and size) stays unchanged across two consecutive animation frames (browser rendering updates). A static button still needs those updates to pass the check.

I isolated the timeout with a plain button page. Playwright found the element, but the click timed out waiting for it to become “stable.” A one-second probe received no animation frame.

GL refers to the OpenGL family of graphics interfaces, which programs use to draw images. Implementations can use graphics hardware or run in software on the main processor. Headless mode has no visible browser window; the browser still needs to render frames.

Bypassing GL startup on Linux

An existing launch workaround, --use-gl=disabled, restored frame delivery. In an existing Playwright setup, merge this setting into the configuration while preserving its other options and launch arguments:

// playwright.config.ts
import { defineConfig } from "@playwright/test";

export default defineConfig({
  testDir: "./e2e",
  use: {
    launchOptions: {
      args: process.platform === "linux" ? ["--use-gl=disabled"] : [],
    },
  },
});

Chromium’s GL startup source recognizes disabled and skips normal OpenGL initialization. That explains what the flag bypasses; the underlying graphics defect remains unidentified. The platform check leaves other platforms’ default launch behavior unchanged.

Verifying a normal click

I kept a five-second regression that supplies its own page, without needing an application server:

// e2e/browser-rendering.spec.ts
import { expect, test } from "@playwright/test";

test("clicks without a graphics-startup stall", async ({ page }) => {
  await page.setContent(
    `<button onclick="this.textContent = 'Clicked'">Ready</button>`,
  );
  await page.getByRole("button", { name: "Ready" }).click({ timeout: 5000 });
  await expect(page.getByRole("button", { name: "Clicked" })).toBeVisible();
});

Run it with pnpm:

pnpm exec playwright test e2e/browser-rendering.spec.ts

The regression timed out before the flag and passed in 194 ms afterward, using the same click checks and deadline. All 25 tests in the browser suite then passed.

What this changes

Ordinary page interactions worked in this mode, but normal GL-backed rendering remained untested. WebGL (browser 3D graphics) and GPU (graphics processor) acceleration need coverage with the graphics backend they are meant to exercise. This is a workaround for the tested environment; Playwright warns that custom browser arguments can break functionality.

References

This post was written with AI assistance.