我扫描了 100 个 Vibe Coding 浏览器游戏源码:AI 最爱写什么?

📅 2026/7/21 6:31:03
我扫描了 100 个 Vibe Coding 浏览器游戏源码:AI 最爱写什么?
前面几篇文章里我讨论过项目质量、自动化审计、浏览器性能以及原型和作品有什么区别。但还有一个问题一直没有用数据回答当 AI 持续参与生成和迭代 100 个同类项目时它到底会反复写出什么是 Canvas 更多还是 DOM 更多多少项目真的有可携带存档多少项目用了音频键盘、触屏、Pointer Events 和响应式 CSS 的覆盖是什么关系不同质量等级的文件体积是否有差异AI 会不会在不同游戏里反复使用相同的函数命名和程序骨架仅凭印象回答这些问题很容易把几个显眼案例当成整个仓库。所以我写了一个静态分析器扫描仓库根目录的 100 个 HTML 游戏页面并把逐文件结果、特征定义、评级分组和限制条件全部输出成 JSON。这篇文章不再介绍某一款游戏也不评价某段代码写得像不像 AI而是把仓库当成一个小型源码语料库来分析。分析范围我究竟扫描了什么本次分析对应仓库提交4db4ddbab9cfd514ad0efb425c074a63eca20aa6扫描对象是仓库根目录的 100 个.html游戏文件。需要先说明三个边界。1. 统计的是根目录单文件版本仓库中还有《星团大作战》《星炉工坊》的前后端增强版本它们使用独立目录、多文件前端和 Node 服务。这些增强版不在本次 100 个根 HTML 的静态语料里。因此后面出现根目录 WebGL 命中为 0并不与已经开源的 WebGL2 增强版矛盾。它只描述这 100 个根页面。2. 这是正则驱动的静态分析不是 AST 和运行时追踪分析器会提取内联script、style再检测getContext(2d)、requestAnimationFramelocalStorage、JSON.stringifyPointer、Touch、Keyboard、Mouse、Click事件Web Audio、Clipboard、TypedArraymedia、ARIA、CSS变量、渐变和阴影导入导出、校验、调试Hook命名函数和addEventListener调用。它可能漏掉动态构造的 API 名称也可能统计到当前没有执行的代码。所以文章使用的是静态命中不是运行时一定执行。3. 内部评级不是市场评分S/A/B/C/D 来自项目自身的内容闭环、重玩性、反馈、双端体验和稳定性评估只用于项目投入决策。它没有用户留存、商业收入或市场传播含义。100 个文件总共多大分析结果首先给出了整个语料的规模指标结果HTML 文件100源码总字节2,887,069 Bytes约合体积2.8MB总行数64,359非空行61,390内联 JavaScript2,173,346 Bytes内联 CSS467,419 Bytes命名函数声明2,587addEventListener 调用458这里最不应该直接使用的是64,359 行代码。因为仓库同时存在完整格式化文件和只有三行的压缩式单文件。同样的逻辑换一种排版就会产生完全不同的行数。因此后续主要使用字节数而不是代码行数比较文件规模。图 1大部分文件很小少数深度升级文件形成明显长尾。总量是源文件总和不是网络传输或打包体积。中位数只有 10.1KB但最大文件超过 300KB文件体积分布如下区间文件数小于 5KB175–15KB4115–30KB2330–60KB560–120KB7大于等于 120KB7也就是说58 款游戏小于 15KB81 款游戏小于 30KB只有 14 款达到 60KB 以上最大文件约 301.1KB。几个分位数是P25 6.9 KB 中位数 10.1 KB P75 27.3 KB 最大 301.1 KB这很符合项目的发展过程早期原型通常只有一段核心规则和简洁 UI被选中深度升级的作品会继续加入章节、角色、构筑、存档迁移、移动端和调试接口于是进入长尾。它不是一个典型文件大约 30KB的均匀仓库而是大量轻量原型加少数持续扩张作品。语料里 75.3% 是 JavaScript按内联块拆分后JavaScript 75.3% 约 2.1MB CSS 16.2% 约 456.5KB HTML 标记 8.5% 约 240.5KB这说明单 HTML主要是一种交付容器而不是大量静态页面标记。游戏状态、规则和绘制逻辑占据绝大部分体积CSS 负责布局和主题真正的 HTML 结构反而最少。渲染路线刚好一半一半100 个根页面的渲染分类是Canvas 2D 50 DOM/CSS 49 DOM SVG 1这组 50:50 不是预先设计的配额但非常接近项目里的两套主要原型路线。Canvas 路线更适合连续移动物理与碰撞射击、竞速和动作大量自由坐标对象需要每帧重绘的游戏画面。50 款 Canvas 2D 游戏中48 款静态命中了requestAnimationFrame。剩余页面可能使用事件驱动绘制或由其他入口间接推进。DOM/CSS 路线更适合卡牌与棋盘经营面板文字推理表格、按钮和分步状态更依赖可访问性和布局的交互。DOM 游戏不一定比 Canvas 简单。例如长线挂机、复杂管理和卡牌构筑可能没有连续画布却拥有更多状态和系统。所以Canvas 数量只能描述渲染方式不能当作复杂度指标。浏览器 API 指纹Click 100Pointer 45Web Audio 14图 2同一文件可以同时命中多个特征。例如 Canvas 游戏既可能有 Pointer Events也会给页面按钮绑定 Click。输入层每个游戏都有 Click但直接游戏输入差异很大输入与界面相关统计特征文件数Click100响应式media98Pointer Events45Keyboard Events40ARIA32直接 Touch Events4Mouse Events2Click 100并不意味着 100 款都只有鼠标操作。它主要说明每个页面都有按钮或可点击 DOM 控件。Pointer Events 同时覆盖鼠标、触控笔和触摸所以只有 4 款直接监听 Touch Events 并不代表只有 4 款支持手机。更值得注意的是另一组差距响应式 CSS 98 Pointer 45 Keyboard 40 ARIA 32几乎所有页面都考虑了窄屏布局但真正需要连续操作的游戏输入适配并不由media自动解决。这也提醒我静态扫描可以告诉我们代码里有没有输入路径却不能证明手势是否舒服、按钮是否容易误触。运行时双视口审计仍然必要。存档层63 款能本地保存25 款能够识别到导入导出状态相关统计形成了一个很明显的漏斗localStorage 63 JSON 序列化 51 Clipboard API 26 明确导入/导出路径 25 校验或 Hash 23这几层分别代表不同成熟度。localStorage设备内记住状态它适合保存最高分、设置和短期进度但数据仍然绑定浏览器和当前设备。JSON开始保存结构化状态51 款使用JSON.stringify或JSON.parse。这通常意味着状态不再只是一个最佳分数而是对象、数组或多字段档案。导入导出档案开始可携带只有 25 款能静态识别到明确导出/导入路径。它们通常属于长线作品需要跨浏览器迁移成长、纪录或断点。校验开始防止损坏和错误粘贴23 款出现了 checksum、hash 或存档码校验语义。这比63 款用了 localStorage更有信息量本地保存很常见真正可迁移、可检查的档案集中在少数长期作品里。但这仍然只是代码特征。是否支持旧版本迁移、非法字段清洗和安全断点还需要阅读具体实现或运行专项测试。视觉层渐变和阴影很常见音频明显更少视觉与反馈相关统计特征文件数CSS/Canvas 渐变64阴影53CSS 自定义属性47Web Audio14CSS Animation10TypedArray9在这个仓库里渐变、阴影和主题色变量远比音频常见。这很容易理解视觉效果可以直接出现在生成结果中截图立即可见声音需要用户手势、开关状态、音量控制和真实播放检查完成成本更高。不过CSS Animation 10也不能解释为只有 10 款有动画。48 款使用requestAnimationFrame大量动画发生在 Canvas而不是 CSS。根目录是高度自包含的0 外部脚本0 module0 fetch分析器没有在根目录 100 个页面中检测到HTTP(S) 外部脚本或样式 0 typemodule 0 fetch() 0 Worker 0 OffscreenCanvas 0 WebGL Context 0这组数字看起来很像AI 的技术偏好但其实主要来自项目约束每款游戏要能直接双击运行不依赖 CDN不需要安装包HTML、CSS 和 JavaScript 封装在同一个文件增强版才单独进入前后端目录。因此正确结论不是AI 不喜欢模块而是在单文件、离线、零外部依赖的任务约束下AI 会稳定回到经典脚本、内联样式和浏览器原生 API。这是文章标题里最需要限制的地方。我们观察到的是AI 在这个仓库约束下最常写什么不是所有 AI 编程项目的普遍分布。高频函数暴露了代码谱系静态分析器还提取了命名函数并按出现于多少个不同文件排序。图 3命名相同不等于语义相同。draw()既可能绘制 Canvas也可能表示抽牌。这张图反映的是代码词汇和谱系不是严格调用图。最常见的命名函数是函数名出现文件数draw84fresh41update41loop40start24save22load21resize19updateHud19makeState16reset16sync16其中draw 84看起来甚至超过了 Canvas 游戏数因为在卡牌项目里draw 还可能表示抽牌。真正有意思的是组合关系makeState / fresh → start / reset → update / loop → draw / render / sync → save / load这套骨架在不同题材中反复出现。它既是浏览器游戏的自然结构也反映了项目代码谱系大量早期作品从相似的生成模板出发后续再根据题材增加规则。fresh在 41 个文件里出现尤其明显。它不是浏览器标准 API也不是所有 JavaScript 项目的惯例更像是同一批代码生成和复用留下的家族特征。这比寻找AI 常用某个变量名更有意义AI 与模板复用往往不会复制完整游戏却会保留状态初始化、循环、渲染和保存的基本语法。2,587 个函数但只有 458 次 addEventListener100 个文件共有命名函数声明 2,587 addEventListener 调用 458458 并不是全部事件绑定数量因为不少页面使用.onclick ...HTML 内联或一次集中委托。它再次说明静态指标必须和检测口径放在一起单独看一个大数字很容易产生错误解释。文件越大内部评级越高r 0.900把文件字节数取log10再与内部评级映射的分数做 Pearson 相关结果是r 0.900从分组中位数看也非常明显评级数量文件体积中位数S10122.2KBA2128.9KBB359.8KBC136.9KBD214.4KB图 4白色竖线是各评级中位数。相关性很强但不能推导增加代码会提高质量。如果只看图很容易得出一个危险结论代码越多质量越高。但这个项目里的因果更可能相反先根据核心循环挑选有潜力的作品对被选中的作品持续升级加入内容、角色、关卡、存档、移动端和测试接口文件体积与内部评级同时上升D 级作品停止投入所以长期保持很小。也就是说体积记录的是投入历史。同样是 120KB一个文件可能拥有完整系统也可能只是重复内容和耦合代码。字节数不理解结构更不理解好玩。评级分组里的功能差异部分特征按评级分组如下评级localStorage导入/导出Web AudioPointerS10101069A211812611B35173010C137015D21110110这里比较有区分力的不是 localStorage而是导入/导出路径。D 级也有 11 款使用本地存储说明保存最佳分数很容易10 款 S 级则全部具备可识别的导入/导出路径因为它们承担更长的成长和纪录。音频也主要集中在 S/A。B 级 35 款中本次检测没有命中 Web Audio。但这依然不是评级公式。短局益智游戏不一定需要跨设备档案DOM 管理游戏也不一定需要 Pointer Events。特征必须服务玩法而不是为了通过表格强行补齐。哪些结果属于 AI哪些不属于写到这里必须把观察拆成四种来源。仓库硬约束0 外部脚本、0 module、0 fetch主要来自单 HTML、离线、零依赖的人为要求不能泛化成 AI 的技术偏好。游戏题材需求Canvas、requestAnimationFrame、键盘和 Pointer Events 的数量首先由动作、射击、棋盘或经营题材决定。AI 与模板复用fresh、makeState、updateHud等重复命名以及相似的初始化——循环——渲染骨架更接近代码生成谱系。后期人工选择和迭代S 级文件体积、可携带存档、音频和调试 Hook 的集中主要来自后续质量投入而不是一次提示词自动生成。所以这次分析没有得出AI 永远喜欢 Canvas或AI 不会写模块这样的结论。更准确的回答是AI 会非常稳定地适应项目约束并在相似任务里复用一套可工作的程序语法最终源码分布是任务要求、题材、生成模板和后期人工取舍共同形成的。分析以后我认为这个仓库最值得调整什么1. 把单 HTML 当作发布格式不一定继续当作唯一开发格式对于 5–15KB 的短局作品单文件非常合适。进入 100KB 以后状态、样式、绘制和存档继续堆在同一作用域定位成本会明显上升。更合理的方向可能是模块化源码 → 构建或内联 → 继续发布零依赖单 HTML用户仍然得到一个可双击文件开发过程却不必拒绝模块边界。2. 不必抽象所有游戏但可以统一基础协议100 款游戏的核心规则差异很大强行共用一个游戏引擎可能制造耦合。更适合统一的是边界协议存档版本与清洗音效开关Pointer/Keyboard 输入适配调试快照命名页面错误和双视口审计导入导出格式。3. 把静态分析变成发布报告而不是只写一次文章以后每轮升级都可以比较哪些 API 覆盖发生变化是否意外引入外部资源哪些文件进入新的体积区间可访问性与调试 Hook 是否提升分析器能否解释新增结构。静态分析不能代替运行测试但适合发现仓库级结构漂移。4. 不为了指标给每款游戏补齐所有功能分析数据最容易带来的副作用是把音频只有 14理解成剩下 86 款都要加音频。短局是否需要存档、纯按钮游戏是否需要 Pointer Events都要回到实际体验。指标用于提出问题不用于统一答案。如何复现这次分析项目地址GitHub - wangzifan396-wzf/mini-browser-games: 100 zero-dependency, single-file HTML5 browser games | 100 款零依赖浏览器小游戏支持桌面/触屏、离线运行与质量分级 · GitHub分析脚本promo-video/scripts/analyze-game-source-corpus.mjs原始 JSONdocs/images/source-corpus-analysis/source-analysis.json配图脚本promo-video/scripts/create-source-corpus-article-images.py运行命令cd promo-video npm.cmd run article:source-corpus:analyze npm.cmd run article:source-corpus:imagesJSON 中包含100 个文件的逐文件字节数、行数和评级全部特征布尔值特征定义和方法限制渲染分类和体积分桶分评级统计高频函数最大与最小文件描述性相关系数。这次分析的限制最后再集中列出不能从数据中推出的结论。静态命中不等于运行执行代码存在不代表玩家一定走到该路径也不能证明实现正确。正则不是完整 JavaScript 解析器动态属性、间接调用和字符串生成可能漏检同名词也可能误判。一个仓库不能代表所有 Vibe Coding这 100 款共享题材范围、单文件要求、开发者偏好和历史模板样本并不独立。体积不代表复杂度或质量格式、注释、重复代码和数据量都会改变字节数。相关性只描述当前仓库。内部评级不是用户结果没有留存、在线人数和商业数据不能根据评级推断市场表现。结语扫描 100 个文件以后我得到的最重要结论不是某个 API 的百分比而是AI 生成的代码从来不是孤立的模型输出它会强烈吸收任务约束、项目模板和后续人工选择。如果只看到0 个 module script很容易说 AI 偏爱老式 JavaScript把项目约束放回来才知道这是离线单文件交付的自然结果。如果只看到 S 级中位数 122.2KB也很容易说代码越多越好把升级历史放回来才知道是高潜力作品获得了更多投入。静态数据真正有价值的地方不是替我们做结论而是迫使结论带上范围、口径和证据。这可能也是 Vibe Coding 项目走向成熟的一个标志不仅能让 AI 继续生成代码还能回过头来测量它究竟生成了什么以及我们自己的选择怎样改变了最终结果。