Browser Checks

Updated April 28, 2026

Browser Checks run a Playwright script in headless Chromium on a schedule. The check fails when an assertion breaks or the global timeout fires, and every run keeps logs, screenshots, video, and a Playwright trace for replay.

Default runtime
2026.05 · Node 24 · Playwright 1.59.1
Legacy runtime
2023.04 · Node 16 · Playwright 1.33
Browser
Chromium, headless
Run timeout
2 minutes per run
Test timeout
30 seconds, configurable
Retention
Logs kept indefinitely
📘When to use Browser Checks over an HTTP monitor

Use a Browser Check when the failure mode is in the rendered page, not the HTTP response. Login flows, search results, multi-step checkouts, and broken JavaScript all return 200 to a plain GET but break for real users.

Create a browser check

Step 01
Open the dashboard
Go to app.hyperping.io and click New monitor.
Step 02
Pick the Browser protocol
Select Browser as the ping protocol and give the monitor a name. Pick the runtime from the runtime card, the default is 2026.05.
Step 03
Write the script and save
Paste a Playwright script into the editor, add any secrets as environment variables, click Run to test, then save to schedule the monitor.

Runtime 2026.05

The 2026.05 runtime is the default for new browser checks. It runs on Node 24 with Playwright 1.59.1 and adds three capabilities on top of the script API.

What 2026.05 adds 3 features

Web VitalsLCP / CLS / TBT / FCP / TTFB
Core Web Vitals are collected from each navigation in your script with no code changes. The dashboard plots them as a time-series chart on the overview, and shows per-run values on every log row.
Step reportingtest.step()
Every test.step('...') call shows up in the run view with a green or red status and its own duration. A failed multi-step check points at the exact stage that broke instead of dropping a wall of logs on the on-call engineer.
Trace and videoretain-on-failure
Failed runs attach a Playwright trace and a video of the run, alongside the screenshot already captured today. The trace deep-links into trace.playwright.dev for step-by-step replay.

Web Vitals

The runtime injects a PerformanceObserver into every page your script opens. After each navigation it captures the five Core Web Vitals and stores them on the run, so you can watch them trend without writing any custom timing code.

LCP
≤ 2,500 ms
Largest Contentful Paint. Time to render the largest visible element. High values mean users wait too long before the most useful part of the page loads.
CLS
≤ 0.1
Cumulative Layout Shift. Visual stability score. Low values mean the layout doesn't jump around as the page loads.
TBT
≤ 200 ms
Total Blocking Time. How long the main thread was blocked from handling user input. High values mean sluggish interactions.
FCP
≤ 1,800 ms
First Contentful Paint. Time until any content is rendered. Large values mean users see a white screen for too long.
TTFB
≤ 800 ms
Time to First Byte. Server response time for the navigation request. Varies by region and network conditions.
📘Where Web Vitals show up

The browser-check overview averages each metric across the picked window into a Web Vitals chart, and every log row shows a vitals strip with the snapshot for that single run. Each value is colored against the thresholds above. Runs that never call page.goto have nothing to report. FID and INP need real user input and are not collected by synthetic checks.

Packages available in 2026.05

Bundled libraries 12 packages

playwright1.59.1
Browser automation API.
@playwright/test1.59.1
Test runner with fixtures, locators, and expect.
typescript^5.6
TypeScript compiler. Use .spec.ts files directly.
expect^29
Standalone Jest-style assertion library.
uuid^9.0.1
RFC4122 UUID generator.
lodash4.17.21
Utility functions for arrays, objects, and collections.
moment^2.30.1
Date and time parsing, formatting, and arithmetic.
axios^1.13.2
HTTP client. Useful for hitting APIs alongside the UI flow.
btoa1.2.1
Base64 encoding helper for Node.
jsonwebtoken^9.0.2
Sign and verify JWTs.
date-fns^2.30.0
Modular date utilities, lighter alternative to moment.
crypto-js^4.2.0
Hashes, HMAC, and symmetric ciphers.

Runtime 2023.04

The 2023.04 runtime runs on Node 16 with Playwright 1.33 and is kept around for existing checks. New checks default to 2026.05 and there's no automatic migration, switch the runtime on the monitor's settings page when you're ready.

Node
16.x
Playwright
playwright ^1.33.0, @playwright/test 1.33
TypeScript
typescript ^5.0.4
Same package set
Lodash, moment, axios, date-fns, crypto-js, jsonwebtoken, uuid, btoa, expect, on the older versions that shipped at the time.
Web Vitals
Not collected on this runtime. Switch to 2026.05 to enable them.

Environment variables

Secrets, tokens, and per-monitor configuration belong in environment variables, not in the script. Add them in the monitor's settings panel below the editor and reference them from the script as process.env.NAME.

Double check

Double check is enabled by default. When a run fails, the runtime immediately retries from a different region before opening an outage. This filters out network blips and one-shot flake without making you wait for the next scheduled run.

Timeouts

Each browser check has a 2-minute global timeout for the entire run. Individual tests inherit Playwright's 30-second default. Override the per-test timeout when you have a slow flow:

test.setTimeoutJS
test.setTimeout(120_000); // 2 minutes in milliseconds

Long flows are usually better split into separate checks, one per critical path, rather than packed into a single 2-minute run.

Run logs and storage

Every run keeps its logs, screenshots, video, and trace indefinitely. Failed runs in 2026.05 attach a video of the run and a Playwright trace to the log row, so you can replay exactly what the browser did.

Example script

A login flow with two assertions, using process.env for credentials:

login.spec.tsTS
import { test, expect } from '@playwright/test';

test.beforeEach(async ({ page }) => {
  await page.goto('https://app.acme.com/login');
  await page.locator('input[type="text"]').type(process.env.EMAIL);
  await page.locator('input[type="password"]').type(process.env.PASSWORD);
  await page.getByRole('button', { name: 'Sign in', exact: true }).click();
});

test('has title', async ({ page }) => {
  await expect(page).toHaveTitle(/Acme/);
});

test('is logged in', async ({ page }) => {
  const content = page.locator('#project-id');
  await expect(content).toHaveText('project_12345');
});

Where to go next