关于 playproof
playproof 是面向开发者和编码 Agent 的浏览器游戏测试工具集。它借助虚拟时钟实现可重复的逐帧检查、脚本化游玩、崩溃检测、设备布局测试和性能测量。
主要功能
- 01确定性逐帧捕获与对比
- 02脚本化输入和分镜运行
- 03可重复的随机崩溃排查
- 04跨设备尺寸的布局检查
- 05帧节奏和性能指标
- 06基于场景的动作快照
适用场景
- 01逐帧检查动画改动
- 02根据记录步骤复现游戏崩溃
- 03检查手机和桌面屏幕布局
- 04在 CI 中运行动作与设备检查
MCP 相关
playproof 也可作为供调用工具的应用使用的 MCP 服务;其工具会直接返回图像供检查。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
6收录的工具列表 · 更新于 5 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| fps | number | 否 | 图集每秒的图块数(默认:适配约 24 个图块) |
| area | string | 否 | 游戏区域的 CSS 选择器(默认值:最大的画布) |
| zoom | string | 否 | 裁剪到运动区域("auto"),或按游戏区域的百分比指定 x,y,w,h |
| ready | string | 否 | 游戏加载完成后为 true 的 JavaScript 表达式 |
| steps | string | 否 | 用 ";" 分隔步骤。步骤(用 ";" 或换行分隔,"#" 开头的行为注释行):wait <time> 等待一段时间(虚拟时间:即时且精确)tap <x,y>|@label 在游戏区域内轻触/点击("50%,80%" 或像素坐标 "120,300")press <x,y> <time> 长按 drag <x,y> <x,y> [time] 滑动/拖动(默认 300ms)key <Key> [time] 按键,例如 Space、ArrowLeft、a、Enter(默认按住 50ms)type <text> 输入文本 click <css selector> 点击 DOM 元素(菜单、HTML 按钮)shot [label] 为故事板拍摄截图 film <time> [fps] 在时间流逝期间捕获帧 until <js expression> [time] 推进时间,直到表达式为真(默认超时 5s)js <js expression> 在页面中执行 JavaScript 并记录结果 时间格式:500、500ms、1.5s。 |
| device | string | 否 | 要模拟的设备:"iPhone 15"、"Pixel 8"、"desktop"(默认值)或 "390x844@3 mobile" |
| target | string | 否 | 游戏的 URL(例如 http://localhost:5173)、文件夹(./dist)或 HTML 文件。若 playproof.config.json 中已设置,则可选。 |
| compare | string | 否 | 用于比较的早期 frames 运行输出文件夹 |
| waitFor | string | 否 | 开始前等待的 CSS 选择器 |
| duration | string | 否 | 录制时长,例如 800ms 或 1.5s(默认值 1s) |
| scenario | string | 否 | playproof.config.json 中场景的名称:提供步骤、时长、缩放和设备 |
详细介绍
编码 Agent 几分钟就能写出一款网页游戏,却无法看着它运行。playproof 让 Agent(也让你)逐帧查看游戏、通过脚本输入操控游戏、搜寻崩溃、检查手机屏幕上的效果,并测量运行流畅度;既可以在桌面上运行,也可以在真实的 Android 手机上运行。所有结果都会以便于阅读的图像形式呈现,并附上简短摘要,指出可能存在的问题。

已使用 Canvas 2D、DOM、Phaser 3、PixiJS 8、three.js、Godot 4 网页导出项目和 Unity WebGL 构建进行测试:每项测试连续运行两次,生成的帧都完全一致(npm run test:engines)。
安装
让你的 Agent 来安装
将以下内容粘贴到 Claude Code、Codex、Cursor、Gemini CLI 或其他编码 Agent 中:
Install playproof from https://github.com/EmirhanOzl/playproof
它会为自己选择下方合适的命令。
Claude Code
claude plugin marketplace add EmirhanOzl/playproof
claude plugin install playproof@playproof
Codex
codex plugin marketplace add EmirhanOzl/playproof
codex plugin add playproof@playproof
Gemini CLI
gemini extensions install https://github.com/EmirhanOzl/playproof
Cursor、OpenCode、GitHub Copilot 和其他 Agent
npx skills add EmirhanOzl/playproof
Claude Desktop 和其他 MCP 应用
对于通过调用工具而不是运行命令来工作的应用,playproof 也是一个 MCP 服务,工具会直接返回图像。将它添加到应用的 MCP 配置中:
{
"mcpServers": {
"playproof": { "command": "npx", "args": ["-y", "playproof", "mcp"] }
}
}
不使用 Agent
无需安装。在游戏文件夹中运行 npx playproof init,即可生成 playproof.config.json(它会查找你的开发服务器、构建文件夹或 HTML 文件);或者直接向任意命令提供开发服务器、构建文件夹或 HTML 文件:
npx playproof frames http://localhost:5173 --device "Pixel 8" --do "tap 50%,50%" --duration 1s
playproof 使用你已安装的 Chrome 或 Edge。如果你希望使用自带的 Chromium,可以运行 npx playproof install-browser 下载。
为什么
截图只能捕捉随机的一瞬间,因此那些让游戏玩起来不对劲的问题往往无从察觉:没有预备动作的跳跃、瞬移的精灵图、只闪烁一帧的画面、按了却毫无反应的按钮、点击一百次后才发生的崩溃,以及在所有手机上都模糊的画布。
playproof 使用虚拟时钟运行游戏。时间只会在 playproof 推进时前进,每次精确推进一帧,因此它可以记录任意时刻,而且每次运行都会生成完全相同的像素。随后,它会将这些帧排列起来,让模型(或人)能够真正查看。由于每次运行都完全一致,Agent 可以记录一段动作、修改代码、再次记录,然后准确地查看修改带来了什么变化。
它能做什么
frames:监视动画
逐帧录制游戏并制作联系表:虚线框标出与上一帧相比发生变化的区域,柱状图显示每一帧中屏幕的变化幅度。playproof 会标记突然跳变(瞬移、补间动画缺失)、单帧闪烁(绘制顺序错误、状态只更新了一半)、停顿(卡住)以及完全没有动静。
npx playproof frames ./dist --device "Pixel 8" --do "tap @Play; wait 400ms; key Space" --duration 700ms

添加 --animate,还可获得一段真实速度的动画 PNG,供你自行观看或添加到拉取请求中。移动物体较小时,添加 --zoom auto:playproof 会以设备的完整分辨率截图,并将每一帧裁剪到动作最集中的区域,让角色的挤压和拉伸效果填满画格。
它还会绘制洋葱皮:在一张图中展示完整动作,最早的时刻最淡。重影之间的间隔体现缓动效果(间隔宽表示速度快,间隔窄表示速度慢),动画师正是通过这些来判断弧线、预备动作或过冲。
frames --compare:查看更改带来的效果
录制一次,修改代码,再使用 --compare <earlier run> 录制一次。较早运行中的每个采样帧都会显示在当前对应帧的上方,并用轮廓标出差异;较早运行的动作轨迹则绘制为新柱状图上的一条线。由于运行结果是确定的,“完全相同”就意味着完全相同。
npx playproof frames http://localhost:5173 --do "tap @Play; key Space" --out .playproof/before
# ...change the jump...
npx playproof frames http://localhost:5173 --do "tap @Play; key Space" --compare .playproof/before

play:运行脚本,生成故事板
npx playproof play http://localhost:5173 --device "Pixel 8" \
--do "shot title; tap @Play; wait 400ms; key Space; film 600ms; until window.playproof.state().screen === 'over' 20s; shot game over; tap @Retry; shot retry"

monkey:随机游玩,寻找崩溃
随机执行点击、拖动、长按、快速三连击、按键以及将标签页切到后台等操作,但可通过种子重复运行。它会报告未捕获的错误、失败的请求、卡死(无限循环)、软锁(持续输入但画面没有变化)以及停止运行的渲染循环。每个错误都附带一条可精确复现问题的命令,整个运行过程会保存为 repro.steps,play --script 会逐个操作重放脚本:删除行,直到只剩下相关操作。
当游戏公开其按钮时(见下方的钩子),monkey 会进行探索:在每个画面中,它会先尝试尚未按过的按钮,再重复之前的操作。screens.png 展示它到达的所有不同画面,让你了解随机玩家能探索到多远,以及哪些地方始终无法到达。
npx playproof monkey ./dist --device "Pixel 8" --steps 300
ERROR Uncaught error: TypeError: Cannot read properties of undefined (reading 'activate')
during: action 13: tap 699,820, wait 119ms
at HTMLCanvasElement.<anonymous> (http://127.0.0.1:52009/:51:40)
Reproduce: playproof monkey "./dist" --seed 1 --steps 13 --device "Pixel 8"
devices:并排测试手机、平板和桌面设备
在九种屏幕上加载游戏,并检查仅在这些设备上出现的布局问题:画布模糊(未按 devicePixelRatio 缩放)、画布拉伸、画布被截断、页面滚动、缺少 viewport meta、缺少 touch-action: none、文本被裁切以及触控目标过小。同一问题在多个设备上出现时只报告一次。
npx playproof devices http://localhost:5173

在真实 Android 手机上、Chrome 中或应用内
frames、play、monkey 和 perf 可通过 --android 在连接 USB 的 Android 手机上运行(使用 Chrome 中的标签页),也可通过 --app <package> 连接到使用 Capacitor、Cordova 或 Ionic 构建的应用内 WebView。playproof 会使用虚拟时钟重新加载应用页面,让你在真实设备上的真实应用中获得相同的帧级精准图像序列;完成后会将页面恢复为正常状态。本地主机端口会自动转发到手机。
npx playproof frames http://localhost:5173 --android --do "tap 50%,60%" --duration 800ms
npx playproof monkey --app com.example.mygame --steps 200
npx playproof perf --app com.example.mygame --duration 10s --monkey
Capacitor 和 Cordova 应用的调试版本默认启用 WebView 调试;其他版本请参阅 webContentsDebuggingEnabled。在 Chrome 中,playproof 会打开自己的标签页,只与该标签页通信,并在完成后关闭它;绝不会列出或读取你的其他标签页。
perf:帧节奏,可在本地或真实手机上运行
在游戏空闲、运行脚本(--do)或随机点击(--monkey)时测量实际帧耗时:fps、p95/p99、相对于显示器刷新率的丢帧数、主线程长任务和堆增长。在本地,CPU 会降速 4×,以近似中端手机的性能。使用 --android 时,它会测量连接 USB 的 Android 手机上运行的 Chrome;本地主机端口会自动转发到手机。
npx playproof perf ./dist --duration 10s --monkey
npx playproof perf http://localhost:5173 --android

场景和 check:动作的快照测试
在 playproof.config.json 中填写游戏默认设置和需要观察的时刻(编辑器会根据架构为其提供自动补全):
{
"$schema": "https://unpkg.com/playproof/schema/config.schema.json",
"target": "http://localhost:5173",
"device": "Pixel 8",
"scenarios": {
"title": { "duration": "800ms" },
"jump": { "do": "tap @Play; wait 400ms; key Space", "duration": "700ms", "zoom": "auto" }
},
"check": { "monkey": { "steps": 200 }, "devices": true }
}
随后可以省略目标,并使用 --scenario jump 替换整个步骤列表:
npx playproof frames --scenario jump
playproof check 会录制每个场景并与 playproof-baselines/ 中的基线进行比较,然后运行 monkey 和 devices。首次运行会写入基线(请将其提交)。此后,任何导致场景动作变化的修改都会使检查失败,并生成 compare.png,准确显示发生的变化;直到你使用 --update 接受更改。基线按操作系统分别保存,因为不同系统的字体和抗锯齿效果有所不同。
npx playproof check # everything
npx playproof check --only jump # one scenario
npx playproof check --update # accept the current motion
在 CI 中
- uses: actions/checkout@v7
- uses: EmirhanOzl/playproof@v0
with:
start: npm run preview
wait-for-url: http://localhost:4173
该操作会安装 Chromium,运行 playproof check(或通过 command 和 args 执行任意命令),将摘要写入作业页面,并将图像作为构件上传。在 CI 中生成一次 Linux 基线(首次运行会写入基线;从构件下载,或在那里运行 --update),确保基线与执行检查的机器匹配。
步骤
由 --do 和 --script <file> 使用(文件中每行一个步骤)。用 ; 分隔步骤。坐标相对于游戏区域(最大的画布,或 --area <selector>)。
| 步骤 | 含义 |
|---|---|
wait 500ms | 让时间流逝(在虚拟时钟上即时且精确) |
tap 50%,80% / tap 120,300 / tap @Play | 点击;@label 使用游戏公开的目标 |
press 50%,50% 800ms | 长按 |
drag 10%,50% 90%,50% 300ms | 滑动 |
key Space [200ms] | 按下按键(默认按住 50 ms) |
type hello | 输入文本 |
click #start | 点击 DOM 元素 |
shot [label] | 为分镜截图 |
film 1s [30] | 时间流逝期间捕获帧 |
until <js> [5s] | 推进时间,直到表达式为真 |
expect <js> | 除非表达式为真,否则运行失败(会显示左侧的值) |
background 5s | 将游戏切换到后台(不生成帧),然后再切回前台 |
js <js> | 求值并记录结果 |
时间:500、500ms、1.5s。
可选:在游戏中添加钩子
这些都不是必需的,但添加四个小函数可以让测试更精确:
window.playproof = {
ready: () => assetsLoaded, // boolean, function or promise: when to start
state: () => ({ screen, level, score }), // logged; used to tell a soft-lock from a busy screen
targets: () => [{ x, y, width, height, label: "Play" }], // CSS px; enables "tap @Play" and aims the monkey at buttons
area: () => ({ x, y, width, height }), // the game world inside a letterboxed canvas; crops every capture
};
如果不添加钩子,可使用 --ready "<js expression>" 或 --wait-for <selector> 等待加载完成,并使用百分比进行点击。examples/jumper 展示了一个包含完整钩子的游戏示例。
工作原理
- 虚拟时钟。 在任何游戏脚本运行之前,playproof 会替换
requestAnimationFrame、setTimeout/setInterval、performance.now和Date,并停止 CSS 动画时间线。时间初始处于冻结状态。推进时间时,计时器和帧会按精确顺序以 60 fps 运行,因此wait 2s只需几毫秒,两次运行也会产生相同的帧。 - 可重复的加载。 页面加载期间,playproof 会自行推进帧。只要游戏有需要真实时间的进行中任务(网络请求、WebAssembly 编译、图像和音频解码、IndexedDB),虚拟时间就会等待这些任务完成,因此慢速设备或连接也能产生与快速设备相同的帧。稳定时间从游戏渲染循环启动时开始计算,也就是 Unity 和 Godot 等引擎启动自身时钟的时刻。
- 带种子的随机性。
Math.random使用种子(--seed,或--seed off)。 - 真实输入。 触屏设备会收到真实的触摸事件(以及 Chrome 根据这些事件生成的指针和鼠标事件);其他设备会收到鼠标事件。按键会保持按下数帧,让通过轮询输入的游戏能够检测到按键。
- 图像,而非日志。 联系表、洋葱皮视图、分镜和图表都在空白 Chromium 页面中绘制,因此 playproof 只有一个依赖项(
playwright-core),不需要原生图像库。
输出
每次运行都会写入 .playproof/<time>-<command>/(或 --out <dir>):图像、包含所有数值的 report.json(逐帧运动、步骤日志、帧时间)、summary.txt,以及保存在 tiles/ 中以供日后比较的采样帧。将 .playproof/ 添加到 .gitignore。--json 会输出报告,而非摘要。
退出代码:0 表示未发现严重问题,1 表示发现错误(崩溃、请求失败、步骤失败、真实手机上的丢帧),2 表示 playproof 本身无法运行。因此,monkey 和 devices 可用作 CI 检查。
要求
- Node.js 22.18 或更高版本。
- 基于 Chromium 的浏览器:已安装的 Chrome 或 Edge,或
npx playproof install-browser。使用--browser chromium|chrome|msedge|<path>选择浏览器。 - 使用
--android和--app时:需要adb(Android platform-tools)、一部允许 USB 调试的手机,并确保屏幕已亮起且处于解锁状态。
限制
- 音频、视频、Web Workers 和 WebAssembly 线程仍使用真实时间;模拟运行在 worker 中的游戏无法做到帧级精确。
- 本地
perf数值来自无 GPU 的模拟:适合比较更改前后,不适合用来承诺帧率。使用--headed进行 GPU 渲染,或使用--android测试真实设备。 - 运动检测结果是启发式判断。突然跳变可能是有意的切镜,单帧闪烁也可能是刻意设计的受击闪光,因此相关描述会将其表述为值得检查的问题。
- 目前仅支持 Chromium。
开发
npm install
node src/cli.ts install-browser # once
npm run check # type check + unit and end-to-end tests
npm run docs:images # regenerate the README images with playproof itself
两个示例游戏也是测试目标:examples/jumper 是一款简洁的小型游戏;examples/buggy 中埋设了八个测试期望 playproof 捕获的错误。
欢迎提交问题和拉取请求。请参阅 CONTRIBUTING.md。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。