Test a playable web game before release, engine-neutral: a timed first-minute check, a measured input and hit registration pass using an in-page probe and a frame analyzer, difficulty and fail state passes, low-end device passes, and bug reports with replay steps a builder can act on. Triggers when someone asks to test, QA, playtest, or check a browser game, verify hit registration or input lag, check game performance on low-end devices, or write up game bugs before a release.
name: playable_web_game_testing description: "Test a playable web game before release, engine-neutral: a timed first-minute check, a measured input and hit registration pass using an in-page probe and a frame analyzer, difficulty and fail state passes, low-end device passes, and bug reports with replay steps a builder can act on. Triggers when someone asks to test, QA, playtest, or check a browser game, verify hit registration or input lag, check game performance on low-end devices, or write up game bugs before a release."
Playable Web Game Testing
Purpose
Most web game testing stops at "it loads and I finished a level". That misses the failures players actually quit over: a first minute spent dismissing popups, a hit that passes through an enemy only when the frame rate drops, a fail state that never fires after a pickup, and a game that runs fine on a desktop and falls apart on a school Chromebook.
This skill runs five separate passes, each with evidence: a cold first-minute pass, a timing pass with a real probe and analyzer, a difficulty and fail state pass, a low-end pass, and a short accessibility pass from the basic Game Accessibility Guidelines. It works for canvas, WebGL, DOM, and engine builds (Phaser, Pixi, Three.js, Unity WebGL, Godot HTML5, hand-rolled) because every check runs against what the player sees and what the browser measures, not against engine internals.
Detailed run sheets live in references/: session-protocol.md for how to run testers, device-and-budgets.md for the device matrix and timing budgets, and bug-reports.md for the report format with three worked reports.
Workflow
- Write the charter before anyone plays. Record build URL and version, the segment under test, device profiles from
references/device-and-budgets.md, and the gates you will judge against. Set gates now, not after seeing results. Minimum gates to name: target frame rate (60 fps means a 16.67 ms frame budget, 30 fps means 33.33 ms), time limit for the first core action in a cold session, event response ceiling for menus, and what counts as a release blocker. Keep passes separate: cold, timing, difficulty, low-end, regression. One capture that mixes segments cannot be read.
- Run the cold first-minute pass. Tester has never seen the game and gets one goal sentence only. Clear saved state first. Ask them to think aloud and do not help; if they stall for 60 seconds, log the time and screen and whether they recover alone. Log timestamps for: page loaded, first input accepted, first core action, first clear feedback or reward, first failure, tutorial end, first unassisted repeat of the core loop. Fail the pass when any of these hold: the first core action lands past your charter gate, a popup or menu chain blocks play before the first input, the goal is not stated or shown in play, the core input is never taught in context, or quitting and restarting is not discoverable. Run 5 fresh testers per round for qualitative findings, fix, then run another round. Report counts ("3 of 5"), never percentages from a sample that small. The full protocol is in
references/session-protocol.md.
- Measure input and hit registration, do not feel it. Load
scripts/probe.js in the game page (paste it in DevTools console or add it to a test build), call __gameProbe.start(), play the most input-heavy segment for at least 10 seconds, call __gameProbe.stop(), then save __gameProbe.download(). Run the capture through the analyzer: python3 bin/analyze_frames.py capture.json (add --target-fps 30 if the game declares 30). The analyzer reports mean, median, p95 and p99 frame time, worst frame, 1 percent low fps, hitch rate, and event timing from PerformanceEventTiming, where input delay is processingStart minus startTime and duration runs from input to next paint. Judge the gates it prints, and keep the capture file with the report.
- Test hit registration where it breaks: at low frame rates. Repeat the core combat, collision, or timing action on the throttled profile (Chrome DevTools Performance panel, CPU 4x slowdown, the setting Google's own DevTools tutorial uses to simulate a mobile CPU) and compare with the unthrottled run using identical inputs and the same save state. A hit system that checks overlap once per rendered frame can miss a fast object when a frame stretches to 50 ms, so test dash-through-target, fast projectile versus small target, jump onto a thin platform, and pickup collection at speed, on both profiles. Any action that works unthrottled and fails throttled is a release blocker with the capture attached, not a tuning note.
- Run the difficulty and fail state pass. Play to fail on purpose at every difficulty offered. Check: every way to lose actually ends play and shows a fail screen with a working retry, the timer running out (including after a pickup or power-up changes the timer), falling out of the world, health reaching zero during an animation or cutscene, and failing during a tutorial. Then check the ramp: a new player should be able to state what got harder between the first and third challenge, assists or difficulty options should change the outcome and not only the label, and a skilled run (no damage, fastest route) should still be possible if the game rewards it. Record attempts to first failure per tester and what caused it. Difficulty verdicts come from observed attempts, not from playing once and declaring it fair.
- Run the low-end pass. Repeat steps 3 to 5 on the desktop throttled profile, a small phone viewport with touch, and a real low-end device if one is available. Emulation and a real device disagreeing is a finding: report both and say which one the verdict rests on. Add the state checks from
references/device-and-budgets.md: tab away and back mid-game, resize or rotate, refresh mid-level, audio unlock on first tap, and quit and relaunch to confirm settings and progress persist. A long session matters too: play 15 minutes on the low-end profile and capture again, because heat and memory growth do not show up in a 30 second run.
- Run the short accessibility pass. Check the basic level of the Game Accessibility Guidelines that a pre-release pass can verify quickly: controls can be remapped or a simpler scheme exists, control sensitivity can be adjusted, interactive elements and touch targets are large and well spaced, no essential information is carried by color alone or by sound alone, speech has subtitles, effects, speech, and music have separate volume controls, there is a wide choice of difficulty, the game can be started without wading through nested menus, text uses simple clear formatting at a readable default size, and settings are saved between sessions. Report each item as present, absent, or not applicable with the screen where you checked it.
- Write bugs a builder can replay. Use the template in
references/bug-reports.md: build, environment, frequency as a count (reproduced 4 of 5 tries), setup and save state, minimized steps with exact inputs and timing, actual result, expected result with its basis, and evidence (capture file name, video timestamp, console errors in full). One defect per report. Describe the failure before any cause, and label cause guesses as guesses. Worked examples for a hit registration miss, a broken fail state, and a blocked first minute are in that file.
- Give the release verdict. End with one verdict per pass: pass, pass with notes, or block, each tied to the evidence that decided it (capture file, analyzer output, session counts). List blockers first, in severity order, each linked to its bug report. State plainly what was not tested: profiles skipped, segments skipped, multiplayer or network conditions not covered. An untested area reported honestly is worth more than a clean verdict that quietly skipped it.
Output Contract
A finished test delivers:
- A charter line: build and version, profiles used, segment coverage, and the gates set before testing.
- A first-minute log: the seven timestamps from step 2, per tester, plus the count of testers blocked or stalled and where.
- Timing evidence: the raw probe capture JSON for each timed segment, the analyzer output for each capture, and the gate verdicts.
- A hit registration comparison: same inputs, same save, unthrottled versus throttled, with reproduction counts for any miss.
- Difficulty and fail state results: every fail condition tried and its outcome, attempts to first failure, and any fail state that did not fire.
- Bug reports in the format from
references/bug-reports.md, one defect each, each with environment, frequency, steps, actual, expected, and evidence.
- A release verdict per pass with blockers listed first, and an explicit list of what was not tested.
Operating Rules
- Evidence beats impression. "Combat felt laggy" is not a finding. A capture showing p95 frame time of 48.6 ms on the throttled profile, plus a hit miss reproduced 4 of 5 tries, is a finding.
- Set gates before the session and print them in the report. Moving a gate after seeing results turns the whole pass into decoration.
- Averages hide stutter. Judge frame time on p95, p99, hitch rate, and 1 percent low, not on average fps alone.
- Never judge the first minute with someone who has seen the game before. Familiar testers cannot un-know where the button is.
- Report small samples as counts. Five testers can surface confusion patterns. They cannot support a percentage, a retention claim, or a difficulty rate for the player base.
- Use the web.dev INP ceiling (200 ms or less is good) for menus and UI response only. In-game hit timing gets its own tighter gate in the charter, derived from the frame budget, because a 200 ms hit response inside play is already broken.
- Separate observation, verdict, and guess in everything you write. The builder needs to know which is which without asking.
- Do not ship a verdict for a profile you did not run. If the low-end pass was skipped, the report says low-end is untested, in the verdict section, not in a footnote.
- Keep raw captures. Numbers quoted without the capture file that produced them cannot be checked or compared after a fix.
- Regression means same segment, same profile, same inputs. Comparing a fixed build on desktop against the old build throttled proves nothing.
Worked example: timing pass on a dash attack
Charter gate: 60 fps target, p95 frame time within 33.33 ms, hitch rate at or under 2 percent, dash attack must hit a stationary enemy at close range on both profiles.
Desktop capture, 640 frames of the Level 2 combat segment: p95 17.9 ms, hitches 0.3 percent, analyzer verdict PASS. Dash attack hit 5 of 5 tries.
Throttled capture (4x CPU slowdown), same segment, same save: p95 48.6 ms, worst frame 83.1 ms, hitches 6.2 percent, analyzer verdict FAIL on both frame gates. Dash attack passed through the enemy 4 of 5 tries, with frame deltas of 49.8 ms and 51.2 ms recorded around the input.
Report outcome: one blocker bug filed using the hit registration example format in references/bug-reports.md, with both captures attached. Release verdict: timing pass BLOCK on the low-end profile, PASS on desktop. The suggested fix direction (sub-step the hit check when a frame exceeds a set length) is written as a labeled guess in the bug report, not as part of the finding.
Tooling
scripts/probe.js runs in the game page and records requestAnimationFrame deltas, PerformanceEventTiming entries, and long task durations where the browser supports the longtask observer. It reports honestly when an observer is unsupported and leaves that list empty instead of filling it in.
bin/analyze_frames.py reads a probe capture (or a bare JSON list of frame deltas, or CSV) and prints frame statistics, event statistics, and PASS or FAIL per gate. Exit code is 0 on pass, 1 on a failed gate, 2 on unreadable or too-short input, so it can run in a release script. Its gates are this skill's gates, tunable with --target-fps, --event-gate-ms, --hitch-ms, and --hitch-rate-max.
Further reading
- MDN, requestAnimationFrame and the Performance APIs: https://developer.mozilla.org/en-US/docs/Web/API/Performance_API and https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEventTiming
- web.dev, Interaction to Next Paint thresholds: https://web.dev/articles/inp
- Chrome for Developers, Analyze runtime performance (CPU 4x slowdown for mobile simulation): https://developer.chrome.com/docs/devtools/performance
- Nielsen Norman Group, How Many Test Users in a Usability Study: https://www.nngroup.com/articles/how-many-test-users/
- Game Accessibility Guidelines, basic level: https://gameaccessibilityguidelines.com/basic/
- Mozilla bug writing guidelines, steps to reproduce with actual and expected results: search "Mozilla bug writing guidelines" for the current copy.
No ratings yet — be the first to rate it.