增长案例库 Maxed 归档 变现与定价AI自动化

如何用浏览器小游戏赚 1.5 万美元:Claude Fable 5 + Claude Code + Poki/CrazyGames

How to Earn $15,000 from Browser Games: Claude Fable 5 + Claude Code + Poki/CrazyGames

中文译文 · 29k 字

一句话摘要

借助 Claude 与游戏平台靠浏览器小游戏赚钱

如何靠网页游戏赚 15,000 美元:Claude Fable 5 + Claude Code + Poki/CrazyGames 2026年6月10日 · 23分钟阅读 · 查看原文 ↗ Games Advertising Claude Side Hustle 在 Poki 和 CrazyGames 上,你赚钱靠的不是卖游戏,而是玩家看到的广告。平台在游戏周围和内部跑广告,并把收入分成。Poki 是 50/50,前提是玩家来自 Poki.com 或它们的营销渠道。CrazyGames 的确切分成比例没有公开宣布,取决于各种因素,但有个诀窍:两个月的发行独占能让你的分成提高大约 50%。 # 游戏:Star Dodger(暂定名) ## 平台与技术栈 - 纯 HTML5 + CSS + 原生 JavaScript。不用框架、不用打包器。 - 结构:index.html、game.js、(可选)放在 /assets 里的资源,使用相对路径。 - 重要(Poki 要求):生产环境不发外部请求——不用 Google Fonts、 不用外部 CDN(jsDelivr 等)、不用外部图片/声音。全部本地化。 - 重要(Poki 要求):能在无痕模式下运行——把任何对 localStorage 的访问 都包进 try/catch。 - 保持初始加载体积小(Poki 要求 < 8 MB)。 - 能从本地文件夹离线运行,也能在门户网站的 iframe 里运行。 ## 类型与玩法机制 - 超休闲躲避街机。 - 玩家控制屏幕底部的一个圆圈。 - 物体从上方落下:红色 = 死亡,黄色 = +分数。 - 目标:尽可能活得久;分数随时间增长。 ## 操控(两者都是必选) - 桌面端:方向键 ← →、A/D、鼠标。 - 移动端/平板:触摸(pointer events),手指水平拖动玩家。 ## 会话经济 - 会话时长:目标 2–5 分钟。无需刷新页面即可快速重开。 - 进度:基于分数提升难度(物体速度/频率)。 ## 广告 —— 通过一个抽象的 window.Ads 接口(别把它绑死到某个门户!) 方法: - Ads.loadingFinished() - Ads.gameplayStart() // 在玩家的第一次输入时调用,而不是加载时 - Ads.gameplayStop() // 在任何暂停/死亡/菜单时调用 - Ads.interstitial(): Promise - Ads.rewarded(): Promise<boolean> // 视频看到最后则返回 true 默认情况下 Ads 是一个桩(stub,打日志 + reward=true)。我稍后再插入门户的实现。 应用:rewarded() 用于"死亡后续命"(每局一次)和"分数翻倍"; interstitial() 用于新一局开始前。 ## 技术要求 - 响应式 canvas,支持 devicePixelRatio(上限 ×2)。首帧要快。 - 干净、带注释的代码,明确标出集成点。 ## 交付物 - 一个能运行的游戏 + 本地运行说明。 - /dist 里的最终构建:index.html 在根目录,相对路径,无外部域名。 核心公式 收入 ≈ (游戏会话数)×(每 1000 次会话的收入)。 每 1000 次会话的收入(开发者侧的 RPM,已经扣掉平台抽成)门户没有官方披露,而且很多开发者签了 NDA,所以下面的数字是规划性估算,不是保证: 游戏质量 RPM 估算 达到 15,000 美元所需的会话数 弱(广告少、受众便宜) 约 2 美元 约 7,500,000 普通(插屏 + 不错的留存) 约 4 美元 约 3,750,000 强(激励视频 + 好的地域) 约 6–8 美元 约 1,900,000 – 2,500,000 结论:15,000 美元大约是 200 万到 700 万次游戏会话。这听起来很多,但门户很大:CrazyGames 大约有每月 3 亿次会话,Poki 大约有每月 9000 万玩家。一个被推荐的爆款就能累积数百万次游玩。所以有两条现实路径: 一个爆款 -> 单个游戏进入首页推荐,贡献绝大部分金额。 一个作品集 -> 5–10 个扎实的游戏,每个贡献一点。没那么依赖运气,但更慢。 什么真正决定钱 不是"做游戏"(有了 Fable 5,这不再是瓶颈),而是: 留存和会话时长 -> 人们玩得越久,看到的广告越多,门户就越积极地推广你。一个真实例子:同一个游戏在 CrazyGames 上能让一个玩家停留约 4.5 分钟并起飞,而在 Poki 上只停留约 2 分钟 -> 于是 Poki 把它下架了。 激励视频 -> 一次自愿的 15–30 秒观看,换取一个奖励。最赚钱的格式。从一开始就把它做进去。 预览点击率(图标 + 标题)-> 整个漏斗的第一个乘数。 加载速度和移动端友好 -> 大部分流量来自手机。 接下来 -> 如何组装出这样一个游戏,并一路把它送到钱面前。 第一部分:Claude Fable 5 -> 访问、规格书、游戏生成 为什么专门用 Fable 5 Fable 5 是第一个公开可用的 Mythos 级模型。对我们的任务,它有一个具体优势:它从一条提示词就能组装出可玩的网页游戏(包括 3D),并且能长时间自主会话 -> 从一份多页简报开始,最长可达十几个小时,自己规划、自己检查、自己返工。对游戏开发来说这意味着:你描述一个概念 -> 你拿到一个能跑的构建,而不是一个片段。 一个细节:Fable 5 有网络安全和生物学的过滤器,会回退到 Opus 4.8。对游戏来说这几乎不会触发 -> 别在打擦边球的措辞上浪费 token 就行。 访问方式 在 Claude 应用里 -> 选择 Fable 5 模型。 通过 API -> 标识符 claude-fable-5。价格:每百万输入 token 10 美元、每百万输出 token 50 美元(带缓存的话输入最多可打九折)。 也在 Amazon Bedrock、Google Cloud 和 Microsoft Foundry 上提供。 对游戏开发来说,最方便的方式是通过 Claude Code(第二部分)跑 Fable 5:它直接在你的磁盘上创建和编辑文件。 原则:不是"提示词",而是"规格书" 要解锁长时间自主运行,别写"做个游戏"。给模型一份规格文档 SPEC.md,让它实现整个东西。额外好处:门户的要求(16:9、无外部请求、无痕模式支持 -> 见第三部分)可以方便地直接烤进规格书,这样你以后就不用返工。 第 1 步:SPEC.md 文件(模板,按你的概念改) # 游戏:Star Dodger(暂定名) ## 平台与技术栈 - 纯 HTML5 + CSS + 原生 JavaScript。不用框架、不用打包器。 - 结构:index.html、game.js、(可选)放在 /assets 里的资源,使用相对路径。 - 重要(Poki 要求):生产环境不发外部请求——不用 Google Fonts、 不用外部 CDN(jsDelivr 等)、不用外部图片/声音。全部本地化。 - 重要(Poki 要求):能在无痕模式下运行——把任何对 localStorage 的访问 都包进 try/catch。 - 保持初始加载体积小(Poki 要求 < 8 MB)。 - 能从本地文件夹离线运行,也能在门户网站的 iframe 里运行。 ## 类型与玩法机制 - 超休闲躲避街机。 - 玩家控制屏幕底部的一个圆圈。 - 物体从上方落下:红色 = 死亡,黄色 = +分数。 - 目标:尽可能活得久;分数随时间增长。 ## 操控(两者都是必选) - 桌面端:方向键 ← →、A/D、鼠标。 - 移动端/平板:触摸(pointer events),手指水平拖动玩家。 ## 会话经济 - 会话时长:目标 2–5 分钟。无需刷新页面即可快速重开。 - 进度:基于分数提升难度(物体速度/频率)。 ## 广告 —— 通过一个抽象的 window.Ads 接口(别把它绑死到某个门户!) 方法: - Ads.loadingFinished() - Ads.gameplayStart() // 在玩家的第一次输入时调用,而不是加载时 - Ads.gameplayStop() // 在任何暂停/死亡/菜单时调用 - Ads.interstitial(): Promise - Ads.rewarded(): Promise<boolean> // 视频看到最后则返回 true 默认情况下 Ads 是一个桩(stub,打日志 + reward=true)。我稍后再插入门户的实现。 应用:rewarded() 用于"死亡后续命"(每局一次)和"分数翻倍"; interstitial() 用于新一局开始前。 ## 技术要求 - 响应式 canvas,支持 devicePixelRatio(上限 ×2)。首帧要快。 - 干净、带注释的代码,明确标出集成点。 ## 交付物 - 一个能运行的游戏 + 本地运行说明。 - /dist 里的最终构建:index.html 在根目录,相对路径,无外部域名。 第 2 步:构建提示词 读 SPEC.md。首先给出一个简短计划:屏幕、文件结构,以及一个带用途的函数清单。 等我说"ok"再说。然后按规格书实现整个项目,创建文件。最后,验证本地运行, 并给出运行命令。 第 3 步:迭代(短小、有针对性) 开头感觉太慢:第一个物体 0.4 秒后才出现——把前 15 秒的难度爬坡调快。 在手机上手指有时会跟丢圆圈。把指针追踪做平滑, 处理 pointercancel 和手指离开边缘的情况。 加一个游戏结束界面:"继续(广告)"通过 Ads.rewarded(),每局一次, "分数翻倍(广告)"和"重新开始"通过 Ads.interstitial()。 在门户上表现好的类型 .io 游戏(即时入场)、超休闲(一个动作)、解谜(长会话)、街机/跑酷(可重玩性)。关键技巧是 Ads 抽象:游戏不知道 Poki/CrazyGames 的存在,所以同一份代码在两个门户上都能跑 -> 你只要插入适配器(第三部分)。 第二部分:Claude Code -> 安装、游戏代码、构建 安装 方法 1 -> 原生安装器(推荐,不需要 Node.js): # Windows (PowerShell) irm https://claude.ai/install.ps1 | iex # macOS / Linux / WSL curl -fsSL https://claude.ai/install.sh | bash # Homebrew (macOS/Linux) brew install --cask claude-code 检查: claude --version 方法 2 -> npm(需要 Node.js 18+,当前 LTS 是 22): npm install -g @anthropic-ai/claude-code 重要:不要用 sudo。如果在 Linux 上遇到权限错误,把 npm 配置成使用 ~/.npm-global,并把它加进你的 PATH。 启动并选择 Fable 5 mkdir star-dodger && cd star-dodger claude 会话内部: /model -> 选择 Claude Fable 5(这就是给你长时间自主运行的东西)。 /help -> 命令,/clear -> 清空上下文,/exit -> 退出。 把 SPEC.md 放进文件夹,给出构建提示词。下面是最终结果,你现在就能跑。 完整游戏代码 index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> <title>Star Dodger</title> <!-- In the portal version, the SDK <script> (Poki/CrazyGames) is added here BEFORE game.js --> <style> html, body { margin: 0; height: 100%; background: #0b0f1a; overflow: hidden; font-family: system-ui, -apple-system, sans-serif; } #game { display: block; width: 100vw; height: 100vh; touch-action: none; } .overlay { position: fixed; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; text-align: center; color: #fff; background: rgba(5,8,16,.72); padding: 20px; } .hidden { display: none; } .btn { background: #3b82f6; color: #fff; border: 0; border-radius: 12px; padding: 14px 22px; font-size: 18px; font-weight: 600; cursor: pointer; min-width: 220px; } .btn.alt { background: #f59e0b; } h1 { font-size: 32px; margin: 0; } p { margin: 0; opacity: .85; line-height: 1.4; } .score { font-size: 22px; } #hud { position: fixed; top: 10px; left: 0; right: 0; text-align: center; color: #fff; font-size: 22px; font-weight: 700; pointer-events: none; text-shadow: 0 1px 4px #000; } </style> </head> <body> <canvas id="game"></canvas> <div id="hud" class="hidden">0</div> <div id="start" class="overlay"> <h1>Star Dodger</h1> <p>Dodge the red ones, catch the yellow ones.<br>Touch/mouse or arrow keys ← →</p> <button class="btn" id="playBtn">Play</button> </div> <div id="over" class="overlay hidden"> <h1>Game Over</h1> <p class="score" id="finalScore">Score: 0</p> <button class="btn alt" id="reviveBtn">Continue (ad)</button> <button class="btn alt" id="doubleBtn">Double the score (ad)</button> <button class="btn" id="againBtn">Restart</button> </div> <!-- Default ad stub. In Part 3 this is replaced by the portal adapter. --> <script> window.Ads = window.Ads || { loadingFinished() { console.log('[ads] loading finished'); }, gameplayStart() { console.log('[ads] gameplay start'); }, gameplayStop() { console.log('[ads] gameplay stop'); }, interstitial() { console.log('[ads] interstitial (stub)'); return Promise.resolve(); }, rewarded() { console.log('[ads] rewarded (stub) -> success'); return Promise.resolve(true); }, }; </script> <script src="game.js"></script> </body> </html> game.js const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); const hud = document.getElementById('hud'); const startScreen = document.getElementById('start'); const overScreen = document.getElementById('over'); const finalScoreEl = document.getElementById('finalScore'); let W, H, dpr; function resize() { dpr = Math.min(window.devicePixelRatio || 1, 2); W = window.innerWidth; H = window.innerHeight; canvas.width = W * dpr; canvas.height = H * dpr; canvas.style.width = W + 'px'; canvas.style.height = H + 'px'; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); } window.addEventListener('resize', resize); resize(); const player = { x: W / 2, y: H - 90, r: 22 }; let items = []; // { x, y, vy, r, type: 'bad' | 'good' } let score = 0, best = 0; let running = false, revivedThisRun = false; let spawnTimer = 0, lastTime = 0; // ---- Controls: touch/mouse (pointer) + keyboard ---- let targetX = null; canvas.addEventListener('pointerdown', e => { targetX = e.clientX; }); canvas.addEventListener('pointermove', e => { if (running) targetX = e.clientX; }); canvas.addEventListener('pointercancel', () => { targetX = null; }); const keys = {}; window.addEventListener('keydown', e => { keys[e.key] = true; if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', ' '].includes(e.key)) e.preventDefault(); }); window.addEventListener('keyup', e => { keys[e.key] = false; }); function reset() { score = 0; items = []; spawnTimer = 0; revivedThisRun = false; player.x = W / 2; player.y = H - 90; targetX = null; } function spawn() { const good = Math.random() < 0.28; items.push({ x: 24 + Math.random() * (W - 48), y: -30, r: good ? 14 : 18 + Math.random() * 10, vy: 140 + score * 1.4 + Math.random() * 60, type: good ? 'good' : 'bad', }); } function update(dt) { player.y = H - 90; const speed = 520; if (keys['ArrowLeft'] || keys['a'] || keys['A']) player.x -= speed * dt; if (keys['ArrowRight'] || keys['d'] || keys['D']) player.x += speed * dt; if (targetX !== null) player.x += (targetX - player.x) * Math.min(1, dt * 12); player.x = Math.max(player.r, Math.min(W - player.r, player.x)); spawnTimer -= dt; const interval = Math.max(0.28, 0.7 - score * 0.004); if (spawnTimer <= 0) { spawn(); spawnTimer = interval; } for (let i = items.length - 1; i >= 0; i--) { const it = items[i]; it.y += it.vy * dt; if (Math.hypot(it.x - player.x, it.y - player.y) < it.r + player.r) { if (it.type === 'good') { score += 5; items.splice(i, 1); continue; } gameOver(); return; } if (it.y - it.r > H) items.splice(i, 1); } score += dt * 8; // points for surviving hud.textContent = Math.floor(score); } function draw() { ctx.clearRect(0, 0, W, H); ctx.fillStyle = '#60a5fa'; ctx.beginPath(); ctx.arc(player.x, player.y, player.r, 0, Math.PI * 2); ctx.fill(); for (const it of items) { ctx.fillStyle = it.type === 'good' ? '#fbbf24' : '#ef4444'; ctx.beginPath(); ctx.arc(it.x, it.y, it.r, 0, Math.PI * 2); ctx.fill(); } } function loop(t) { if (!running) return; const dt = Math.min(0.05, (t - lastTime) / 1000 || 0); lastTime = t; update(dt); if (running) { draw(); requestAnimationFrame(loop); } } function startGame() { reset(); startScreen.classList.add('hidden'); overScreen.classList.add('hidden'); hud.classList.remove('hidden'); running = true; lastTime = performance.now(); Ads.gameplayStart(); // player's first input (Poki requirement) requestAnimationFrame(loop); } function resume() { overScreen.classList.add('hidden'); hud.classList.remove('hidden'); running = true; lastTime = performance.now(); Ads.gameplayStart(); requestAnimationFrame(loop); } function gameOver() { running = false; Ads.gameplayStop(); // any stop of gameplay best = Math.max(best, Math.floor(score)); finalScoreEl.textContent = `Score: ${Math.floor(score)} (best ${best})`; document.getElementById('reviveBtn').style.display = revivedThisRun ? 'none' : ''; document.getElementById('doubleBtn').style.display = ''; hud.classList.add('hidden'); overScreen.classList.remove('hidden'); } // ---- Buttons + ads ---- document.getElementById('playBtn').onclick = startGame; document.getElementById('againBtn').onclick = async () => { await Ads.interstitial(); // interstitial ad before a new run startGame(); }; document.getElementById('reviveBtn').onclick = async () => { const ok = await Ads.rewarded(); // rewarded video if (ok) { revivedThisRun = true; items = items.filter(it => it.type === 'good' || it.y < H * 0.4); // clear nearby threats resume(); } }; document.getElementById('doubleBtn').onclick = async () => { const ok = await Ads.rewarded(); if (ok) { score *= 2; best = Math.max(best, Math.floor(score)); finalScoreEl.textContent = `Score: ${Math.floor(score)} (best ${best})`; document.getElementById('doubleBtn').style.display = 'none'; } }; Ads.loadingFinished(); // assets here are instant 本地运行与测试 在项目文件夹里: python3 -m http.server 8000 打开 http://localhost:8000。然后: 在 DevTools(F12)里开启设备模式(手机/平板)-> 测试触摸。 在无痕模式下打开游戏 -> Poki 会检查这个(那里 localStorage 受限)。 双击 index.html 有时会弄坏脚本 -> 本地服务器更可靠。 最终构建 把发布构建组装进 /dist:index.html 在根目录,game.js 挨着它,资源在 /dist/assets,所有路径相对,无外部域名。移除调试代码和日志。 验证初始加载体积 < 8 MB,且游戏能从 /dist 运行。 常见问题 command not found: claude -> PATH 没刷新。重启终端或改用原生安装器。 npm 权限错误 -> 别用 sudo;配置 ~/.npm-global。 双击时脚本不工作 -> 通过本地服务器跑。 游戏不是全屏 -> 检查 width: 100vw; height: 100vh 以及 resize() 里的 canvas 重算。 第三部分:Poki / CrazyGames -> SDK 集成、要求、发布 多亏了 Ads 抽象,我们不用动游戏代码。我们做两个入口文件 -> poki.html 和 crazygames.html -> 各自加载自己的 SDK,并在加载 game.js 之前定义自己的 window.Ads。 A. Poki 文档:https://sdk.poki.com 检查器:https://inspector.poki.dev 联系:[email protected] 条款:Poki 来源流量的收入 50/50;所有游戏都要经过人工筛选;要求网页独占(Steam/手机端没问题);Poki for Developers 处于封闭测试 -> 通过表单申请。 游戏要求(审核时检查)-> 清单: 支持桌面、移动和平板,全屏游戏(竖屏/横屏)。 16:9 宽高比,缩放到 640×360 / 836×470 / 1031×580,填满整个 canvas。 无痕模式 -> localStorage 访问放进 try/catch。 无外部请求 -> Poki 默认阻止对第三方服务的调用(Google Fonts、外部 CDN/资源)。游戏必须自包含(我们的代码正是如此)。 无第三方广告或开屏,无外部链接;工作室 logo -> 只能放在加载画面(广告通过 Poki SDK 跑)。 游戏在启用广告拦截器时也能玩 -> 别把玩法藏在广告拦截器提示后面(Poki 在自己那侧处理)。 初始加载体积 < 8 MB,干净构建、无调试代码。 静态和动画图标(全球发布两者都必选)。 别让游戏的滚动把父页面带得跳来跳去。 poki.html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> <title>Star Dodger</title> <!-- 1) Poki SDK BEFORE the game code --> <script src="https://game-cdn.poki.com/scripts/v2/poki-sdk.js"></script> <style>/* ... the same styles as in index.html ... */</style> </head> <body> <canvas id="game"></canvas> <div id="hud" class="hidden">0</div> <!-- ... the same start / over overlays ... --> <!-- 2) Ads adapter for Poki (INSTEAD of the stub) --> <script> function muteForAd() { /* stop music/sound and disable game input */ } function unmuteForAd() { /* restore sound and input */ } window.Ads = { loadingFinished() { PokiSDK.gameLoadingFinished(); }, gameplayStart() { PokiSDK.gameplayStart(); }, gameplayStop() { PokiSDK.gameplayStop(); }, // Won't ALWAYS show a video — Poki decides based on its own timer interstitial() { return PokiSDK.commercialBreak(muteForAd).then(unmuteForAd); }, // success === true only if the video was watched to the end rewarded() { return PokiSDK.rewardedBreak({ size: 'medium', onStart: muteForAd }) .then((success) => { unmuteForAd(); return !!success; }); }, }; PokiSDK.init() .then(() => console.log('Poki SDK ok')) .catch(() => console.log('Poki SDK failed, continue anyway')); </script> <!-- prevent the page from jumping on arrows/space/wheel (the game is inside Poki's long page) --> <script> window.addEventListener('keydown', ev => { if (['ArrowDown', 'ArrowUp', ' '].includes(ev.key)) ev.preventDefault(); }); window.addEventListener('wheel', ev => ev.preventDefault(), { passive: false }); </script> <!-- 3) Game code, unchanged --> <script src="game.js"></script> </body> </html> SDK 事件规则(Poki 严格检查这些): gameplayStart() -> 在玩家的第一次输入时,不是加载时。 gameplayStop() -> 在任何停止时(暂停、菜单、关卡结束、死亡)。 事件不能连续触发两次(start 之后又 start、stop 之后又 stop 是错误)。 广告期间,不要调用 SDK 事件。 commercialBreak() -> 只在从暂停退出回到玩法时。 我们的代码已经符合:startGame/resume → gameplayStart,gameOver → gameplayStop,而且它们之间总有一个 stop。 关于 16:9 宽高比。基础代码拉伸到整个窗口(任何形状),而 Poki 要求 16:9 加缩放。加一个逻辑 16:9 场景和黑边(letterbox)-> 给 Claude Code 的提示词: 做一个 640x360(16:9)的逻辑游戏场景,按比例缩放、居中, 带黑边(letterbox),同时保持对任何窗口尺寸和 devicePixelRatio 的支持。重算输入坐标以考虑缩放。 Poki 发布漏斗(不只是"上传"): 上传到 Poki for Developers(先接受条款与条件)。 试玩 / 反馈 -> 真实玩家如何互动(几小时内出结果)。 玩家适配测试(Player Fit Test)-> 玩家喜不喜欢这个游戏。 网页适配测试(Web Fit Test)-> 游戏在开放网页上怎么活、怎么变现。这里图标/预览至关重要(有封面图和动画图标的 A/B 测试)。如果不通过 -> 看他们的"如果我的游戏没成功该怎么办"章节(换个新图标/标题重试,常常能救回来)。 最终 Poki 审核 -> 最终检查与发布。 B. CrazyGames 文档:https://docs.crazygames.com 提交:https://developer.crazygames.com/games 付款:https://docs.crazygames.com/payouts 条款:约每月 3 亿次会话;标准分成不公开披露;2 个月发行独占把分成提高约 50%;sitelock 自动启用(游戏只在 CrazyGames 及其关联站上运行);支持通用 HTML5 SDK v3;只接受压缩构建(Brotli/Gzip)-> dev 构建跑不了。 crazygames.html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> <title>Star Dodger</title> <!-- 1) CrazyGames SDK v3 BEFORE the game code --> <script src="https://sdk.crazygames.com/crazygames-sdk-v3.js"></script> <style>/* ... the same styles ... */</style> </head> <body> <canvas id="game"></canvas> <div id="hud" class="hidden">0</div> <!-- ... the same overlays ... --> <!-- 2) Ads adapter for CrazyGames + loading the game after init --> <script> function muteForAd() { /* disable sound + game input */ } function unmuteForAd() { /* restore sound + input */ } (async () => { try { await window.CrazyGames.SDK.init(); // you must wait for init } catch (e) { console.log('CrazyGames init failed', e); } const CG = window.CrazyGames.SDK; window.Ads = { loadingFinished() { CG.game.sdkGameLoadingStop?.(); }, gameplayStart() { CG.game.gameplayStart(); }, gameplayStop() { CG.game.gameplayStop(); }, // requestAd takes callbacks, not a Promise — we wrap it in a Promise interstitial() { return new Promise((resolve) => { CG.ad.requestAd('midgame', { adStarted: () => muteForAd(), adFinished: () => { unmuteForAd(); resolve(); }, adError: () => { unmuteForAd(); resolve(); }, // on error, continue anyway }); }); }, // grant the reward ONLY in adFinished rewarded() { return new Promise((resolve) => { CG.ad.requestAd('rewarded', { adStarted: () => muteForAd(), adFinished: () => { unmuteForAd(); resolve(true); }, // watched -> reward adError: () => { unmuteForAd(); resolve(false); }, // didn't show -> no reward }); }); }, }; // load the game after the adapter is ready const s = document.createElement('script'); s.src = 'game.js'; document.body.appendChild(s); })(); </script> </body> </html> 注意:广告以覆盖层显示 -> 务必在 adStarted 里静音、在 adFinished/adError 里恢复。游戏在启用广告拦截器时也必须保持可玩。 CrazyGames 付款:通过 Tipalti 按月发放。提交游戏前,你必须在开发者门户(计费部分)完成付款 onboarding -> 否则你没法提交游戏。付款方式和门槛取决于你的国家 -> 详情在付款页面上。(这不是税务/财务建议 -> 有疑问时咨询专业人士。) CrazyGames 发布: 一个 developer.crazygames.com 上的开发者账户 + 付款 onboarding。 SDK v3(上面)、一个压缩构建。 在 developer.crazygames.com/games 提交:封面图、描述、标签 → 质量审核。 C. 一份代码库 -> 两个门户 star-dodger/ ├── game.js ← 游戏逻辑,共享(不变) ├── index.html ← 本地版本(Ads 桩)——用于开发/测试 ├── poki.html ← Poki 版本 └── crazygames.html ← CrazyGames 版本 让 Claude Code 生成两个版本的提示词: 基于 index.html 和 game.js,组装 poki.html 和 crazygames.html。在每一个里: 在 <head> 加载所需 SDK,在我们的接口之上实现 window.Ads (loadingFinished/gameplayStart/gameplayStop/interstitial/rewarded),然后加载 game.js。 不要改 game.js。实现:Poki —— commercialBreak/rewardedBreak;CrazyGames —— SDK.ad.requestAd('midgame'/'rewarded') 带回调。广告期间静音声音/输入, Poki 还要加上防页面跳动的片段。 D. 真正带来钱的是什么 代码只是地基。钱是由这些东西挣来的: 图标 + 标题(CTR)-> 测试变体;Poki 有封面图和动画图标的 A/B 测试。 前 30 秒 -> 立刻清晰、上瘾,没有冗长教程。 会话时长和留存 -> 门户用它决定是否推广你。 在自然节点(继续 / 翻倍)的激励视频 -> 最赚钱的格式。 加载速度 -> 轻资源、快速首帧、体积 < 8 MB。 基于分析迭代 -> 看门户的指标 → 在 Claude Code 里调 → 更新游戏。 第四部分:结论 Fable 5 到来后改变的是什么:"做游戏"不再昂贵和缓慢。过去需要一个团队的事,现在一次会话就能从一条提示词组装出来。原型的入场门槛急剧下降 -> 那就是你的机会窗口。 但老实说,关于那 15,000 美元:工具栈本身不带来钱。Fable 5 + Claude Code 让你便宜、快速地做出一个游戏;Poki/CrazyGames 给你巨大的受众和现成的变现。这笔钱归结为一件事:你是不是做出了人们真正想玩的东西。瓶颈已经从"做"转移到了"人们会玩什么、谁会看到它"。 一份逐周路线 第 1 周:安装 Claude Code,选 Fable 5,从 SPEC.md 组装 1–2 个简单游戏,内置激励视频的位置。目标不是杰作,而是一个可玩的构建。 第 2 周:做 poki.html / crazygames.html,跑一遍需求清单(16:9、无痕、< 8 MB、无外部请求),打磨前 30 秒和加载,准备图标。 第 3–4 周:上传到 Poki 检查器 / 到 CrazyGames,通过测试(Player Fit → Web Fit)。给弱的游戏换个包装(图标/标题)或替换掉。给强的游戏加码并推广。 之后:别把一切都押在一个游戏上:建一个作品集,便宜地尝试很多概念,在会话起飞的地方加倍下注。15,000 美元要么来自一个被推荐的爆款,要么来自几个扎实的游戏加起来达到数百万次会话。 感谢你读完这份指南,你会成功的,我知道的 🩷 把它存进书签,别弄丢了 标签:# X # Games # Advertising # Claude # Side Hustle # Side Hustle # Marketing # Growth # AI # Guide # Fable 相关文章 你这个周末做了个手机游戏。这是为什么——以及如何用一个文件修好它。游戏做完了。墙不是代码。是之后的一切。Games AI Claude Advertising

原文参考:https://maxed.wiki/posts/how-to-earn-15-000-from-browser-games-claude-fable-5-claude-code-poki-crazygames/ (Maxed.wiki,本页为站内中文整理)