HTML5 game publishing

How to Automatically Detect Broken HTML5 Games on Your Website

A working Playwright script, a GitHub Actions schedule and webhook alerts to find games that no longer load, with the limits of each check explained.

PlayKovo Team5 min read
Games currently available on PlayKovo. Open a title to see its current listing.

Games break without you touching anything. A provider moves a file, a certificate expires, an update adds a script error, or an embed starts refusing connections. If you run a site with hundreds of games, you will not find these by clicking around, and visitors who hit a dead game rarely come back.

This guide shows how to find broken games automatically: what to check, a working Playwright script, how to run it on a schedule, and how to send yourself an alert.

What "broken" looks like

FailureHow to detect it
Page returns 404, 500 or times outHTTP status of the game's embed URL
Embed refuses to connectThe browser shows an error page instead of the game
Script crashes on loadUncaught page errors (pageerror events)
A required file is missingFailed script, stylesheet or document requests
Blank or black screenThe screenshot looks empty

Why a plain HTTP check is not enough

  • A server can return 200 for a page whose script then crashes and leaves a black screen.
  • Some game hosts treat requests that do not come from a real browser differently. A script using plain fetch or curl may be refused or served a different page, so it will report a healthy game as broken. Verify with a real browser.
  • Frames can be blocked by headers such as X-Frame-Options or a frame-ancestors policy, which only show up when a browser actually tries to render them.

So use two layers: a cheap status check on everything, then a browser check on the games that matter or that failed the first layer.

Step 1: get the list of games

Keep a list of embed URLs. If you use the PlayKovo catalog, the feed already provides them: playkovo.com/api/embed-games returns games[] with a slug and an embedUrl for each game.

Step 2: the Playwright script

Install once:

npm init -y
npm install playwright
npx playwright install chromium

Save as check-games.mjs:

import { chromium } from 'playwright';

const FEED = process.env.FEED_URL || 'https://playkovo.com/api/embed-games';
const LIMIT = Number(process.env.LIMIT || 20);
const WAIT_MS = Number(process.env.WAIT_MS || 8000);
const WEBHOOK = process.env.ALERT_WEBHOOK_URL; // optional Slack-style webhook

const feed = await (await fetch(FEED)).json();
const games = feed.games.slice(0, LIMIT);

const browser = await chromium.launch();
const failures = [];

for (const game of games) {
  const context = await browser.newContext({ viewport: { width: 960, height: 540 } });
  const page = await context.newPage();
  const problems = [];

  page.on('pageerror', (err) => problems.push('script error: ' + err.message.slice(0, 120)));
  page.on('response', (res) => {
    const type = res.request().resourceType();
    if (res.status() >= 400 && ['document', 'script', 'stylesheet'].includes(type)) {
      problems.push(`HTTP ${res.status()} for ${type}: ${res.url().slice(0, 100)}`);
    }
  });

  try {
    const response = await page.goto(game.embedUrl, { waitUntil: 'load', timeout: 30000 });
    if (!response || response.status() >= 400) {
      problems.push('page status ' + (response ? response.status() : 'none'));
    }
    await page.waitForTimeout(WAIT_MS);
    const shot = await page.screenshot();
    // A blank screen compresses to a very small PNG. Tune this number for your games.
    if (shot.length < 6000) problems.push('screen looks blank');
  } catch (err) {
    problems.push('load failed: ' + err.message.split('\n')[0]);
  }

  if (problems.length) failures.push({ slug: game.slug, url: game.embedUrl, problems });
  await context.close();
}

await browser.close();

console.log(`${games.length} checked, ${failures.length} with problems`);
for (const f of failures) console.log('-', f.slug, f.problems.join(' | '));

if (failures.length && WEBHOOK) {
  const text = `Game check: ${failures.length}/${games.length} failed\n` +
    failures.map((f) => `${f.slug}: ${f.problems[0]}`).join('\n');
  await fetch(WEBHOOK, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ text })
  });
}

process.exit(failures.length ? 1 : 0);

Run it with node check-games.mjs. Set LIMIT=200 to check more games, and raise WAIT_MS for heavy 3D games.

What the script does and does not tell you

  • It loads each game in a real Chromium browser, waits, records uncaught errors, failed document, script and stylesheet requests, and checks that the screen is not empty.
  • Some games show a start screen that waits for a click; the screenshot is still not blank, so that counts as healthy.
  • Some games send failed requests for ad or analytics scripts that do not affect play. If you get noise, only report failures from the game's own domain.
  • The blank-screen test is a heuristic. A solid-colour start screen can look blank, so treat that message as "look at this one", not as proof.
  • It loads the game page directly. If your site embeds the game in an iframe, also check one of your own pages that contains the embed, since embedding rules can differ.

Step 3: run it on a schedule

A GitHub Actions workflow is the simplest way. Save as .github/workflows/check-games.yml:

name: Check games
on:
  schedule:
    - cron: '0 6 * * *'   # every day at 06:00 UTC
  workflow_dispatch:

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm install playwright
      - run: npx playwright install --with-deps chromium
      - run: node check-games.mjs
        env:
          LIMIT: '200'
          ALERT_WEBHOOK_URL: ${{ secrets.ALERT_WEBHOOK_URL }}

When the script exits with code 1, the workflow shows as failed and GitHub can email you. The webhook secret sends the list of broken games to Slack or any chat tool that accepts a JSON text field.

Step 4: decide what happens to a broken game

  1. Re-check before acting. One failure can be a slow server, so require two failures in a row.
  2. Hide the game from listings and your sitemap while it is broken, so visitors and search engines do not land on a dead page.
  3. If the game comes from a provider, tell them; if it is gone permanently, remove the page.
  4. Keep a short log of what broke and when. Patterns show you which providers are unreliable.

Checklist

  • A list of embed URLs that you can update automatically.
  • A daily browser-based check, not just fetch.
  • Alerts that reach you without checking a dashboard.
  • A rule for hiding games after repeated failures.
  • Manual testing of a few games on a real phone every month; automation does not cover touch controls.

Fewer dead pages also helps your search performance; see why your game website has hundreds of games but almost no traffic. For ready-to-embed games, see the catalog import guide.

We use optional cookies for site analytics and site advertising. You can change your choice later. Privacy policy.