Loading...
Loading...
Full guided pipeline — scaffold, design, audio, deploy, and monetize a game from scratch
npx skill4agent add opusgamelabs/game-creator make-gamegit pushTaskCreateTaskUpdateTaskTaskbrowser_navigateclaude mcp add playwright npx @playwright/mcp@latestTaskYou are the QA subagent for the game creation pipeline.Project path:Dev server port:<project-dir>Step being verified:<port><step name>Run these phases in order. Stop early if a phase fails critically (build or runtime).Phase 1 — Build Checkbashcd <project-dir> && npm run buildIf the build fails, report FAIL immediately with the error output.Phase 2 — Runtime Checkbashcd <project-dir> && node scripts/verify-runtime.mjsIf the runtime check fails, report FAIL immediately with the error details.Phase 3 — Gameplay Verificationbashcd <project-dir> && node scripts/iterate-client.js \ --url http://localhost:<port> \ --actions-file scripts/example-actions.json \ --iterations 3 --screenshot-dir output/iterateAfter running, read the state JSON files () and error files (output/iterate/state-*.json):output/iterate/errors-*.json
- Scoring: At least one state file should show
score > 0- Death: At least one state file should show
. Mark as SKIPPED (not FAIL) if game_over is not reached — some games have multi-life systems or random hazard spawns that make death unreliable in short iterate runs. Death SKIPPED is acceptable and does not block the pipeline.mode: "game_over"- Errors: No critical errors in error files
Skip this phase ifis not present.scripts/iterate-client.jsPhase 4 — Architecture Validationbashcd <project-dir> && node scripts/validate-architecture.mjsReport any warnings but don't fail on architecture issues alone.Phase 5 — Visual Review via Playwright MCP Use Playwright MCP to visually review the game. If MCP tools are not available, fall back to reading iterate screenshots from.output/iterate/With MCP:
tobrowser_navigatehttp://localhost:<port> — wait 2 seconds for the game to loadbrowser_wait_for — save asbrowser_take_screenshotoutput/qa-gameplay.png- Assess: Are entities visible? Is the game rendering correctly?
- Check safe zone: Is any UI hidden behind the top ~8% (Play.fun widget area)?
- Check entity sizing: Is the main character large enough (12–15% screen width for character games)?
- Wait for game over (or navigate to it),
— save asbrowser_take_screenshotoutput/qa-gameover.png- Check buttons: Are button labels visible? Blank rectangles = broken button pattern.
- Check mute button: Is there a mute toggle visible? If not, flag as ISSUE.
Screenshot timeout: Ifhangs for more than 10 seconds (can happen with continuous WebGL animations), cancel and proceed with code review instead. Do not let a screenshot hang block the entire QA phase.browser_take_screenshotNote on iterate screenshots: The iterate-client useswhich returns blank/black images when Phaser uses WebGL withcanvas.toDataURL(). Always prefer Playwright MCP viewport screenshots (preserveDrawingBuffer: false) over iterate screenshots for visual review.browser_take_screenshotWithout MCP (fallback):
- Read the iterate screenshots from
(may be black if WebGL — this is expected)output/iterate/shot-*.png- Fall back to code review: read scene files and assess visual correctness from the code
Return your results in this exact format (text only, no images):QA RESULT: PASS|FAIL Phase 1 (Build): PASS|FAIL Phase 2 (Runtime): PASS|FAIL Phase 3 (Gameplay): Iterate PASS|FAIL, Scoring PASS|FAIL|SKIPPED, Death PASS|FAIL|SKIPPED, Errors PASS|FAIL Phase 4 (Architecture): PASS — N/N checks Phase 5 (Visual): PASS|FAIL — <issues if any> ISSUES: - <issue descriptions, or "None"> SCREENSHOTS: output/qa-gameplay.png, output/qa-gameover.png
Launch QA subagent → read text result
If PASS → proceed to next step
If FAIL → launch autofix subagent with ISSUES list → re-run QA subagent
Max 3 attempts per stepoutput/autofix-history.jsonissuefix_attemptedresult: "failure"Taskoutput/autofix-history.jsonoutput/autofix-history.json{ "step": "<step name>", "issue": "<what failed>", "fix_attempted": "<what was tried>", "result": "success|failure", "timestamp": "<ISO date>" }$ARGUMENTS2d3d$ARGUMENTSx.com/*/status/*twitter.com/*/status/*fxtwitter.com/*/status/*vxtwitter.com/*/status/*fetch-tweethttps://api.fxtwitter.com/<user>/status/<id>WebFetchFound tweet from @handle: "Tweet text..."I'll build a 2D game based on this: [your creative interpretation as a game concept] Game name:<generated-name>Sound good?
character-library/manifest.jsonhasCelebrities = trueprogress.md"a cartoon caricature of <Name>, <distinguishing features>, low poly game character"3d-character-library/manifest.jsonfind-3d-asset.mjsMESHY_API_KEYI'll generate custom 3D models with Meshy AI for the best results. You can get a free API key in 30 seconds:
- Sign up at https://app.meshy.ai
- Go to Settings → API Keys
- Create a new API key
What is your Meshy API key? (Or type "skip" to use generic model libraries instead)
meshy-generate.mjsTaskCreateoutput/output/autofix-history.json[]in_progress~/.claude/plugins/cache/local-plugins/game-creator/1.0.0/templates/templates/game-creatorCLAUDE.md.claude-plugin/plugin.jsonexamples/examples/<game-name>/<game-name>/templates/phaser-2d/<target-dir>/templates/threejs-3d/<target-dir>/package.json"name"<title>index.htmlnode --version && npm --versionexport NVM_DIR="$HOME/.nvm" && source "$NVM_DIR/nvm.sh"npm installnpx playwright --versionnode_modules/.bin/playwright --versionnpm install -D @playwright/testnpx playwright install chromiumscripts/verify-runtime.mjsscripts/iterate-client.jsscripts/example-actions.jsonverifyiteratepackage.jsonnpm run devvite.config.jslsof -i :<port> -tvite.config.jsscripts/verify-runtime.mjsPORTTaskYou are implementing Step 1 (Scaffold) of the game creation pipeline.Project path:Engine:<project-dir>Game concept:<2d|3d>Skill to load:<user's game description>(2D) orphaser(3D)threejs-gameCore loop first — implement in this order:
- Input (touch + keyboard from the start — never keyboard-only)
- Player movement / core mechanic
- Fail condition (death, collision, timer)
- Scoring
- Restart flow (GameState.reset() → clean slate)
Keep scope small: 1 scene, 1 mechanic, 1 fail condition. Wire spectacle EventBus hooks alongside the core loop — they are scaffolding, not polish.Transform the template into the game concept:
- Rename entities, scenes/systems, and events to match the concept
- Implement core gameplay mechanics
- Wire up EventBus events, GameState fields, and Constants values
- Ensure all modules communicate only through EventBus
- All magic numbers go in Constants.js
- No title screen — the template boots directly into gameplay. Do not create a MenuScene or title screen. Only add one if the user explicitly asks.
- No in-game score HUD — the Play.fun widget displays score in a deadzone at the top of the game. Do not create a UIScene or HUD overlay for score display.
- Mobile-first input: Choose the best mobile input scheme for the game concept (tap zones, virtual joystick, gyroscope tilt, swipe). Implement touch + keyboard from the start — never keyboard-only. Use the unified analog InputSystem pattern (moveX/moveZ) so game logic is input-source-agnostic.
- Force portrait for vertical games: For dodgers, runners, collectors, and endless fallers, set
in Constants.js. This locks portrait layout on desktop (pillarboxed with black bars viaFORCE_PORTRAIT = true). Use fixed design dimensions (540×960), not conditionalScale.FIT + CENTER_BOTH._isPortrait ? 540 : 960- Visible touch indicators required: Always render semi-transparent arrow buttons (or direction indicators) on touch-capable devices. Use capability detection (
), NOT OS detection (('ontouchstart' in window) || (navigator.maxTouchPoints > 0)). Enable pointer events (pointerdown/pointermove/pointerup) on ALL devices — never gate behinddevice.os.android || device.os.iOS. UseisMobileconstants from Constants.js for sizing.TOUCH- Minimum 7–8% canvas width for collectibles/hazards: Items smaller than 7% of
become unrecognizable blobs on phone screens. Size attacks at ~9%, power-ups at ~7%, player character at 12–15%.GAME.WIDTH- Wire spectacle events: emit
inSPECTACLE_ENTRANCE,create()on every player input,SPECTACLE_ACTIONon score/destroy,SPECTACLE_HITon consecutive hits (passSPECTACLE_COMBO),{ combo }at milestones (5, 10, 25 — passSPECTACLE_STREAK),{ streak }on close callsSPECTACLE_NEAR_MISSVisual identity — push the pose:
- If the player character represents a real person or brand, build visual recognition into the entity from the start. Don't use generic circles/rectangles as placeholders — use descriptive colors, proportions, and features that communicate identity even before pixel art is added.
- Named opponents/NPCs must have visual presence on screen — never text-only. At minimum use distinct colored shapes that suggest the brand. Better: simple character forms with recognizable features.
- Collectibles and hazards must be visually self-explanatory. Avoid abstract concepts ("imagination blocks", "creativity sparks"). Use concrete objects players instantly recognize (polaroids, trophies, lightning bolts, money bags, etc.).
- Think: "Could someone screenshot this and immediately know what the game is about?"
- NEVER use a single letter (C, G, O) as a character's visual identity
- NEVER differentiate two characters only by fill color — they must have distinct silhouettes and features
- When a company is featured (OpenAI, Anthropic, xAI, etc.), use the CEO as the character: Altman for OpenAI, Amodei for Anthropic, Musk for xAI, Zuckerberg for Meta, Nadella for Microsoft, Pichai for Google, Huang for NVIDIA
- Add entrance sequence in
: player starts off-screen, tweens into position withcreate(), landing shake + particle burstBounce.easeOut- Add combo tracking to GameState:
(current streak, resets on miss),combo(session high), both reset inbestComboreset()- Ensure restart is clean — test mentally that 3 restarts in a row would work identically
- Add
to GameState for mute supportisMutedCRITICAL — Preserve the button pattern:
- The template's
contains a workingGameOverScene.jshelper (Container + Graphics + Text). Do NOT rewrite this method. Keep it intact or copy it into any new scenes that need buttons. The correct z-order is: Graphics first (background), Text second (label), Container interactive. If you put Graphics on top of Text, the text becomes invisible. If you make the Graphics interactive instead of the Container, hover/press states break.createButton()Character & entity sizing:
- Character WIDTH from
, HEIGHT fromGAME.WIDTH * ratio(whereWIDTH * SPRITE_ASPECTfor 200×300 spritesheets). Never define character HEIGHT asconst SPRITE_ASPECT = 1.5— on mobile portrait,GAME.HEIGHT * ratiois much larger thanGAME.HEIGHT, squishing characters.GAME.WIDTH- For character-driven games (named personalities, mascots, famous figures): make the main character prominent —
toGAME.WIDTH * 0.12(12–15% of screen width). Use caricature proportions (large head = 40–50% of sprite height, exaggerate distinguishing features) for personality games.GAME.WIDTH * 0.15- Non-character entities (projectiles, collectibles, squares) can use
for both dimensions since they have no intrinsic aspect ratio to preserve.GAME.WIDTH * ratioPlay.fun safe zone:
- Import
fromSAFE_ZONE. All UI text, buttons, and interactive elements (title text, score panels, restart buttons) must be positioned belowConstants.js. The Play.fun SDK renders a 75px widget bar at the top of the viewport (z-index 9999). UseSAFE_ZONE.TOPfor proportional positioning within the usable area (wheresafeTop + usableH * ratio).usableH = GAME.HEIGHT - SAFE_ZONE.TOPGenerate game-specific test actions: After implementing the core loop, overwritewith actions tailored to this game. Requirements:scripts/example-actions.json
- Use the game's actual input keys (e.g., ArrowLeft/ArrowRight for dodger, space for flappy, w/a/s/d for top-down)
- Include enough gameplay to score at least 1 point
- Include a long idle period (60+ frames with no input) to let the fail condition trigger
- Total should be at least 150 frames of gameplay
Example for a dodge game (arrow keys):json[ {"buttons":["ArrowRight"],"frames":20}, {"buttons":["ArrowLeft"],"frames":20}, {"buttons":["ArrowRight"],"frames":15}, {"buttons":[],"frames":10}, {"buttons":["ArrowLeft"],"frames":20}, {"buttons":[],"frames":80} ]Example for a platformer (space to jump):json[ {"buttons":["space"],"frames":4}, {"buttons":[],"frames":25}, {"buttons":["space"],"frames":4}, {"buttons":[],"frames":25}, {"buttons":["space"],"frames":4}, {"buttons":[],"frames":80} ]Before returning, write:<project-dir>/design-brief.md# Design Brief ## Concept One-line game concept. ## Core Mechanics For each mechanic: - **Name**: what it does - **State field**: which GameState field it affects - **Expected magnitude**: how much/fast it should change (e.g., "reaches 50-70% of max within the round duration without player input") ## Win/Lose Conditions - How the player wins - How the player loses - Confirm both outcomes are realistically achievable with the current Constants.js values ## Entity Interactions For each visible entity (enemies, projectiles, collectibles, environmental objects): - **Name**: what it is - **Visual identity**: what it should LOOK like and why (reference real logos, people, objects — not abstract concepts) - **Distinguishing feature**: the ONE exaggerated feature visible at thumbnail size (e.g., "curly dark hair + glasses" for Amodei, "leather jacket" for Jensen Huang) - **Real image asset**: logo URL to download, or "pixel art" if no real image applies - **Behavior**: what it does (moves, falls, spawns, etc.) - **Player interaction**: how the player interacts with it (dodge, collect, tap, block, or "none — background/decoration") - **AI/opponent interaction**: how the opponent interacts with it, if applicable For named people: describe hair, glasses, facial hair, clothing. For companies: specify logo to download. NEVER use a letter or text label as visual identity. ## Expression Map For each personality character, map game events to expressions: ### Player: [Name] | Game Event | Expression | Why | |---|---|---| | Idle/default | normal | Resting state | | Score point / collect item | happy | Positive reinforcement | | Take damage / lose life | angry | Visceral reaction | | Power-up / special event | surprised | Excitement | | Win / game over (high score) | happy | Celebration | | Lose / game over (low score) | angry | Defeat | ### Opponent: [Name] | Game Event | Expression | Why | |---|---|---| | Idle/default | normal | Resting state | | Player scores | angry | Frustrated at losing | | Opponent scores | happy | Gloating | | Near-miss / close call | surprised | Tension |Do NOT start a dev server or run builds — the orchestrator handles that.
progress.mdsrc/core/EventBus.jssrc/core/Constants.jssrc/entities/src/core/GameState.jsprogress.md# Progress
## Game Concept
- **Name**: [game name from project]
- **Engine**: Phaser 3 / Three.js
- **Description**: [from user's original prompt]
## Step 1: Scaffold
- **Entities**: [list entity names from src/entities/]
- **Events**: [list event names from EventBus.js]
- **Constants keys**: [top-level sections from Constants.js, e.g. GAME, PLAYER, ENEMY, COLORS]
- **Scoring system**: [how points are earned, from GameState + scene logic]
- **Fail condition**: [what ends the game]
- **Input scheme**: [keyboard/mouse/touch controls implemented]
## Decisions / Known Issues
- [any notable decisions or issues from scaffolding]Your game is scaffolded and running! Here's how it's organized:
— all game settings (speed, colors, sizes)src/core/Constants.js — how parts of the game talk to each othersrc/core/EventBus.js — tracks score, lives, etc.src/core/GameState.js- Mobile controls are built in — works on phone (touch/tilt) and desktop (keyboard)
Next up: pixel art. I'll create custom pixel art sprites for every character, enemy, item, and background tile — all generated as code, no image files needed. Then I'll add visual polish on top.
completedin_progressdesign-brief.mdcharacter-library/manifest.jsoncharacter-library/manifest.json~/.claude/plugins/cache/local-plugins/game-creator/*/character-library/manifest.jsonmkdir -p <project-dir>/public/assets/characters/<slug>/
cp <plugin-root>/character-library/characters/<slug>/sprites/* \
<project-dir>/public/assets/characters/<slug>/"<Name> portrait photo""<Name> face""<Name> smiling""<Name> laughing""<Name> angry""<Name> serious stern""<Name> surprised""<Name> shocked"crop-head.mjsnormal.jpghappy.jpg<project-dir>/public/assets/characters/<slug>/raw/node <plugin-root>/scripts/build-character.mjs "<Name>" \
<project-dir>/public/assets/characters/<slug>/ --skip-findraw/normal.pnghappy.pngcp raw/normal.png raw/angry.png # fill missing with normal
cp raw/normal.png raw/surprised.pngbuild-character.mjsbuild-character.mjsgame-assetsprogress.md"<Name>: pixel art fallback — no photo-composite available"progress.md## Characters
- trump: Tier 1 (pre-built, 4 expressions)
- karpathy: Tier 3 (1 image found, duplicated to 4 slots)
- some-ceo: Tier 5 (pixel art fallback)TaskYou are implementing Step 1.5 (Pixel Art Sprites) of the game creation pipeline.Project path:Engine: 2D (Phaser 3) Skill to load:<project-dir>game-assetsReadat the project root before starting. It describes the game's entities, events, constants, and scoring system from Step 1.progress.mdCharacter library sprites are already copied to. For personality characters, load the spritesheet and wire expression changes per the game-assets skill's "Expression Wiring Pattern". Addpublic/assets/characters/<slug>/andEXPRESSIONto Constants.js. Wire expression changes to EventBus events per the Expression Map inEXPRESSION_HOLD_MS.design-brief.mdFollow the game-assets skill fully for non-personality entities:
- Read all entity files (
) to findsrc/entities//generateTexture()callsfillCircle()- Choose the palette that matches the game's theme (DARK, BRIGHT, or RETRO)
- Create
— thesrc/core/PixelRenderer.js+renderPixelArt()utilitiesrenderSpriteSheet()- Create
with the chosen palettesrc/sprites/palette.js- Create sprite data files (
,player.js,enemies.js,items.js) with pixel matricesprojectiles.js- Create
with background tiles (ground variants, decorative elements)src/sprites/tiles.js- Create or update the background system to use tiled pixel art instead of flat colors/grids
- Update entity constructors to use pixel art instead of geometric shapes
- Add Phaser animations for entities with multiple frames
- Adjust physics bodies for new sprite dimensions
Character prominence: If the game features a real person or named personality, use the Personality Character (Caricature) archetype — 32x48 grid at scale 4 (renders to 128x192px, ~35% of canvas height). The character must be the visually dominant element on screen. Supporting entities stay at Medium (16x16) or Small (12x12) to create clear visual hierarchy.Push the pose — thematic expressiveness:
- Sprites must visually embody who/what they represent. A sprite for "Grok AI" should look like Grok (logo features, brand colors, xAI aesthetic) — not a generic robot or colored circle.
- For real people: exaggerate their most recognizable features (signature hairstyle, glasses, facial hair, clothing). Recognition IS the meme hook.
- For brands/products: incorporate logo shapes, brand colors, and distinctive visual elements into the sprite design.
- For game objects: make them instantly recognizable. A "power-up" should look like the specific thing it represents in the theme, not a generic star or diamond.
- Opponents should be visually distinct from each other — different colors, shapes, sizes, and personality. A player should tell them apart at a glance.
Self-audit before returning — check every personality sprite against these:
- Does each sprite have distinct hair (not a solid-color dome)?
- Does each sprite have facial features beyond just eyes (glasses, facial hair, or clothing details if applicable)?
- Would two character sprites look different if rendered in the same color?
- Is any
being used as the primary identifier? If so, remove it and add physical features instead.scene.add.text()- Does the head region (rows 0-28) use at least 4 distinct palette indices?
- For brand entities: was a real logo downloaded and loaded? If not, why?
After completing your work, append asection to## Step 1.5: Assetswith: palette used, sprites created, any dimension changes to entities.progress.mdDo NOT run builds — the orchestrator handles verification.
design-brief.md# Step A: Generate the character model
MESHY_API_KEY=<key> node <plugin-root>/scripts/meshy-generate.mjs \
--mode text-to-3d \
--prompt "a stylized <character description>, low poly game character, full body" \
--polycount 15000 --pbr \
--output <project-dir>/public/assets/models/ --slug <character-slug>
# Step B: Read the refineTaskId from meta, then rig immediately
# The rig command auto-downloads walk/run GLBs as <slug>-walk.glb and <slug>-run.glb
REFINE_ID=$(python3 -c "import json; print(json.load(open('<project-dir>/public/assets/models/<character-slug>.meta.json'))['refineTaskId'])")
MESHY_API_KEY=<key> node <plugin-root>/scripts/meshy-generate.mjs \
--mode rig --task-id $REFINE_ID --height 1.7 \
--output <project-dir>/public/assets/models/ --slug <character-slug><slug>.glbloadAnimatedModel()SkeletonUtils.clone()<slug>-walk.glb<slug>-run.glb"a cartoon caricature of Trump, blonde hair, suit, red tie, low poly game character, full body"3d-character-library/manifest.jsoncp <plugin-root>/3d-character-library/models/<model>.glb \
<project-dir>/public/assets/models/<slug>.glbfind-3d-asset.mjsnode <plugin-root>/scripts/find-3d-asset.mjs \
--query "<character name> animated character" \
--max-faces 10000 --list-only3d-character-library/design-brief.md# With Meshy (preferred) — generate each prop
MESHY_API_KEY=<key> node <plugin-root>/scripts/meshy-generate.mjs \
--mode text-to-3d \
--prompt "a <entity description>, low poly game asset" \
--polycount 5000 \
--output <project-dir>/public/assets/models/ --slug <entity-slug>
# Without Meshy — search free libraries
node <plugin-root>/scripts/find-3d-asset.mjs --query "<entity description>" \
--source polyhaven --output <project-dir>/public/assets/models/progress.md## 3D Characters
- knight (player): Tier 1 — Meshy AI generated + rigged (idle/walk/run)
- goblin (enemy): Tier 1 — Meshy AI generated + rigged (idle/walk/run)
## 3D Assets
- tree: Meshy AI generated (static prop)
- barrel: Meshy AI generated (static prop)
- house: Poly Haven fallback (CC0)TaskYou are implementing Step 1.5 (3D Assets) of the game creation pipeline.Project path:Engine: 3D (Three.js) Skill to load:<project-dir>andgame-3d-assetsmeshyaiReadat the project root before starting. It lists generated/downloaded models and the character details.progress.mdRigged character GLBs + animation GLBs are already in. Set up the character controller:public/assets/models/
- Create
— CRITICAL: usesrc/level/AssetLoader.jsfor rigged models (regularSkeletonUtils.clone()breaks skeleton bindings → T-pose). Import from.clone().three/addons/utils/SkeletonUtils.js- Add
config toMODELSwith:Constants.js(rigged GLB),path,walkPath,runPath,scaleper model. Start withrotationY— most Meshy models face +Z and need flipping.rotationY: Math.PI- For each rigged model:
- Load with
, createloadAnimatedModel()AnimationMixer- Load walk/run animation GLBs separately, register their clips as mixer actions
- Log all clip names:
console.log('Clips:', clips.map(c => c.name))- Store mixer and actions in entity's
userData- Call
every framemixer.update(delta)- Use
pattern for smooth transitionsfadeToAction()- For static models (ring, props): use
(regular clone)loadModel()- Orientation & scale verification (MANDATORY):
- After loading each model, log its bounding box size
- Compute auto-scale to fit target height and container bounds
- Align feet to floor:
position.y = -box.min.y- Characters must face each other / the correct direction — adjust
in ConstantsrotationY- Characters must fit inside their environment (ring, arena, platform)
- Position characters close enough to interact (punch range, not across the arena)
- Add primitive fallback in
for every model load.catch()After completing your work, append asection to## Step 1.5: 3D Assetswith: models used (Meshy-generated vs library), scale/orientation adjustments, verified facing directions.progress.mdDo NOT run builds — the orchestrator handles verification.
Your game now has pixel art sprites and backgrounds! Every character, enemy, item, and background tile has a distinct visual identity. Here's what was created:
— rendering enginesrc/core/PixelRenderer.js — all sprite data, palettes, and background tilessrc/sprites/
Your game now has custom 3D models! Characters were generated with Meshy AI (or sourced from the model library), rigged, and animated with walk/run/idle. Props and scenery are loaded from GLB files. Here's what was created:
— model loader with SkeletonUtilssrc/level/AssetLoader.js — Meshy-generated and/or library GLB modelspublic/assets/models/- OrbitControls camera with WASD movement
Next up: visual polish. I'll add particles, screen transitions, and juice effects. Ready?
completedin_progressTaskYou are implementing Step 2 (Visual Design — Spectacle-First) of the game creation pipeline.Project path:Engine:<project-dir>Skill to load:<2d|3d>game-designerReadat the project root before starting. It describes the game's entities, events, constants, and what previous steps have done.progress.mdApply the game-designer skill with spectacle as the top priority. Work in this order:1. Opening Moment (CRITICAL — this determines promo clip success):
- Entrance flash:
on scene startcameras.main.flash(300)- Player slam-in: player starts off-screen, tweens in with
, landing shake (0.012) + particle burst (20 particles)Bounce.easeOut- Ambient particles active from frame 1 (drifting motes, dust, sparkles)
- Optional flavor text (e.g., "GO!", "DODGE!") — only when it naturally fits the game's vibe
- Verify: the first 3 seconds have zero static frames
2. Every-Action Effects (wire to SPECTACLE_ events from Step 1):*
- Particle burst (12-20 particles) on
andSPECTACLE_ACTIONSPECTACLE_HIT- Floating score text (28px, scale 1.8,
) onElastic.easeOutSCORE_CHANGED- Background pulse (additive blend, alpha 0.15) on
SCORE_CHANGED- Persistent player trail (particle emitter following player,
)blendMode: ADD- Screen shake (0.008-0.015) on hits
3. Combo & Streak System (wire to SPECTACLE_COMBO / SPECTACLE_STREAK):
- Combo counter text that scales with combo count (32px base, +4px per combo)
- Streak milestone announcements at 5x, 10x, 25x (full-screen text slam + 40-particle burst)
- Hit freeze frame (60ms physics pause) on destruction events
- Shake intensity scales with combo (0.008 + combo * 0.002, capped at 0.025)
4. Standard Design Audit:
- Full 10-area audit (background, palette, animations, particles, transitions, typography, game feel, game over, character prominence, first impression / viral appeal)
- Every area must score 4 or higher — improve any that fall below
- First Impression / Viral Appeal is the most critical category
5. Intensity Calibration:
- Particle bursts: 12-30 per event (never fewer than 10)
- Screen shake: 0.008 (light) to 0.025 (heavy)
- Floating text: 28px minimum, starting scale 1.8
- Flash overlays: alpha 0.3-0.5
- All new values go in Constants.js, use EventBus for triggering effects
- Don't alter gameplay mechanics
After completing your work, append asection to## Step 2: Designwith: improvements applied, new effects added, any color or layout changes.progress.mdDo NOT run builds — the orchestrator handles verification.
Your game looks much better now! Here's what changed: [summarize changes]Next up: promo video. I'll autonomously record a 50 FPS gameplay clip in mobile portrait — ready for social media. Then we'll add music and sound effects.
completedin_progressffmpeg -version | head -1FFmpeg is not installed. Skipping promo video. Install it with(macOS) orbrew install ffmpeg(Linux), then runapt install ffmpeglater./game-creator:promo-video
completedcp <plugin-root>/skills/promo-video/scripts/convert-highfps.sh <project-dir>/scripts/
chmod +x <project-dir>/scripts/convert-highfps.shTaskYou are implementing Step 2.5 (Promo Video) of the game creation pipeline.Project path:Dev server port:<project-dir>Skill to load:<port>promo-videoReadand the following source files to understand the game:progress.md
— find the death/failure method(s) to patch outsrc/scenes/GameScene.js — understand event flowsrc/core/EventBus.js — check input keys, game dimensionssrc/core/Constants.js — verifysrc/main.jsand__GAME__are exposed__GAME_STATE__Createfollowing thescripts/capture-promo.mjsskill template. You MUST adapt these game-specific parts:promo-video
Death patching — identify ALL code paths that lead to game over and monkey-patch them. Search for,triggerGameOver,gameOver,takeDamage,playerDied, or any method that setsonPlayerHit. Patch every one.gameState.gameOver = true Input sequence — determine the actual input keys from the game's input handling (look for,createCursorKeys(),addKeys(), etc.). Generate ainput.on('pointerdown')function that produces natural-looking gameplay for this specific game type:generateInputSequence(totalMs)
- Dodger (left/right): Alternating holds with variable timing, occasional double-taps
- Platformer (jump): Rhythmic taps with varying gaps
- Shooter (move + fire): Interleaved movement and fire inputs
- Top-down (WASD): Figure-eight or sweep patterns
Entrance pause — include a 1-2s pause at the start so the entrance animation plays (this is the visual hook). Viewport — always(9:16 mobile portrait) unless the game is desktop-only landscape.{ width: 1080, height: 1920 } Duration — 13s of game-time by default. For slower-paced games (puzzle, strategy), use 8-10s.Config: The script must accept,--port, and--durationCLI args with sensible defaults.--output-dirDo NOT run the capture — just create the script. The orchestrator runs it.
# Ensure output directory exists
mkdir -p <project-dir>/output
# Run capture (takes ~26s for 13s game-time at 0.5x)
node scripts/capture-promo.mjs --port <port>
# Convert to 50 FPS MP4
bash scripts/convert-highfps.sh output/promo-raw.webm output/promo.mp4 0.5output/promo.mp4DESIRED_GAME_DURATION / 1000ffmpeg -y -ss 5 -i output/promo.mp4 -frames:v 1 -update 1 output/promo-thumbnail.jpgPromo video recorded! 50 FPS, mobile portrait (1080x1920).File:([duration]s, [size])output/promo.mp4This was captured autonomously — the game ran at 0.5x, recorded at 25 FPS, then FFmpeg sped it up to 50 FPS. Death was patched out so it shows continuous gameplay.Next up: music and sound effects. Ready?
completedin_progressTaskYou are implementing Step 3 (Audio) of the game creation pipeline.Project path:Engine:<project-dir>Skill to load:<2d|3d>game-audioReadat the project root before starting. It describes the game's entities, events, constants, and what previous steps have done.progress.mdApply the game-audio skill:
- Audit the game: check for
, read EventBus events, read all scenes@strudel/web- Install
if needed@strudel/web- Create
,src/audio/AudioManager.js,music.js,sfx.jsAudioBridge.js- Add audio events to EventBus.js (including
)AUDIO_TOGGLE_MUTE- Wire audio into main.js and all scenes
- Important: Use explicit imports from
(@strudel/web) — do NOT rely on global registrationimport { stack, note, s } from '@strudel/web'- Mute toggle: Wire
toAUDIO_TOGGLE_MUTE. Add M key shortcut and a speaker icon UI button. See thegameState.game.isMutedrule and the game-audio skill "Mute Button" section for requirements and drawing code.mute-buttonAfter completing your work, append asection to## Step 3: Audiowith: BGM patterns added, SFX event mappings, mute wiring confirmation.progress.mdDo NOT run builds — the orchestrator handles verification.
Your game now has music and sound effects! Click/tap once to activate audio, then you'll hear the music. Note: Strudel is AGPL-3.0, so your project needs a compatible open source license.Next up: deploy to the web. I'll help you set up GitHub Pages so your game gets a public URL. Future changes auto-deploy when you push. Ready?
completedin_progressgh auth statusghYou need the GitHub CLI to deploy. Install it with:
- macOS:
brew install gh- Linux:
or see https://cli.github.comsudo apt install ghOnce installed, runand follow the prompts, then tell me when you're ready.gh auth login
ghYou need to log in to GitHub. Run this command and follow the prompts:gh auth loginChoose "GitHub.com", "HTTPS", and authenticate via browser. Tell me when you're done.
gh auth statusnpm run builddist/index.htmlvite.config.jsbase/<repo-name>/export default defineConfig({
base: '/<game-name>/',
// ... rest of config
});git init
git add .
git commit -m "Initial commit"
gh repo create <game-name> --public --source=. --pushnpm install -D gh-pages
npx gh-pages -d distdist/gh-pagesGITHUB_USER=$(gh api user --jq '.login')
gh api repos/$GITHUB_USER/<game-name>/pages -X POST --input - <<< '{"build_type":"legacy","source":{"branch":"gh-pages","path":"/"}}'GITHUB_USER=$(gh api user --jq '.login')
GAME_URL="https://$GITHUB_USER.github.io/<game-name>/"
echo $GAME_URLcurl -s -o /dev/null -w "%{http_code}" "$GAME_URL"deploypackage.json{
"scripts": {
"deploy": "npm run build && npx gh-pages -d dist"
}
}Your game is live!URL:https://<username>.github.io/<game-name>/How auto-deploy works: Whenever you make changes, just run:npm run deployOr if you're working with me, I'll commit your changes and run deploy for you.Next up: monetization. I'll register your game on Play.fun (OpenGameProtocol), add the points SDK, and redeploy. Players earn rewards, you get a play.fun URL to share on Moltbook. Ready?
completedin_progressnode skills/playdotfun/scripts/playfun-auth.js statusnode skills/playdotfun/scripts/playfun-auth.js callback &To register your game on Play.fun, you need to log in once. Open this URL in your browser: https://app.play.fun/skills-auth?callback=http://localhost:9876/callbackLog in with your Play.fun account. Credentials are saved locally. Tell me when you're done.
playfun-auth.js statushttps://<username>.github.io/<game-name>/package.jsonsrc/core/Constants.jsplaydotfunPOST https://api.play.fun/games{
"name": "<game-name>",
"description": "<game-description>",
"gameUrl": "<deployed-url>",
"platform": "web",
"isHTMLGame": true,
"iframable": true,
"maxScorePerSession": <based on game scoring>,
"maxSessionsPerDay": 50,
"maxCumulativePointsPerDay": <reasonable daily cap>
}maxScorePerSession: 100-500maxScorePerSession: 500-2000maxScorePerSession: 1000-5000# Read API key from agent config (stored by playfun-auth.js)
# Example path for Claude Code — adapt for your agent
API_KEY=$(cat ~/.claude.json | jq -r '.mcpServers["play-fun"].headers["x-api-key"]')
echo "User API Key: $API_KEY"index.html</head><meta name="x-ogp-key" content="<USER_API_KEY>" />
<script src="https://sdk.play.fun/latest"></script>x-ogp-keysrc/playfun.js// src/playfun.js — Play.fun (OpenGameProtocol) integration
import { eventBus, Events } from './core/EventBus.js';
const GAME_ID = '<game-uuid>';
let sdk = null;
let initialized = false;
export async function initPlayFun() {
const SDKClass = typeof PlayFunSDK !== 'undefined' ? PlayFunSDK
: typeof OpenGameSDK !== 'undefined' ? OpenGameSDK : null;
if (!SDKClass) {
console.warn('Play.fun SDK not loaded');
return;
}
sdk = new SDKClass({ gameId: GAME_ID, ui: { usePointsWidget: true } });
await sdk.init();
initialized = true;
// addPoints() — call frequently during gameplay to buffer points locally (non-blocking)
eventBus.on(Events.SCORE_CHANGED, ({ score, delta }) => {
if (initialized && delta > 0) sdk.addPoints(delta);
});
// savePoints() — ONLY call at natural break points (game over, level complete)
// WARNING: savePoints() opens a BLOCKING MODAL — never call during active gameplay!
eventBus.on(Events.GAME_OVER, () => { if (initialized) sdk.savePoints(); });
// Save on page unload (browser handles this gracefully)
window.addEventListener('beforeunload', () => { if (initialized) sdk.savePoints(); });
}| Method | When to use | Behavior |
|---|---|---|
| During gameplay | Buffers points locally, non-blocking |
| Game over / level end | Opens blocking modal, syncs buffered points to server |
savePoints()initPlayFun()src/main.jsimport { initPlayFun } from './playfun.js';
// After game init
initPlayFun().catch(err => console.warn('Play.fun init failed:', err));cd <project-dir> && npm run build && npx gh-pages -d distYour game is monetized on Play.fun!Play:Play.fun:<game-url>https://play.fun/games/<game-uuid>The Play.fun widget is now live — players see points, leaderboard, and wallet connect. Points are buffered during gameplay and saved on game over.Share on Moltbook: Post your game URL to moltbook.com — 770K+ agents ready to play and upvote.
completedYour game has been through the full pipeline! Here's what you have:
- Scaffolded architecture — clean, modular code structure
- Pixel art sprites — recognizable characters (if chosen) or clean geometric shapes
- Visual polish — gradients, particles, transitions, juice
- Promo video — 50 FPS gameplay footage in mobile portrait (
)output/promo.mp4- Music and SFX — chiptune background music and retro sound effects
- Quality assured — each step verified with build, runtime, and visual review
- Live on the web — deployed to GitHub Pages with a public URL
- Monetized on Play.fun — points tracking, leaderboards, and wallet connect
Share your play.fun URL on Moltbook to reach 770K+ agents on the agent internet. Post your promo video to TikTok, Reels, or X to drive traffic.What's next?
- Add new gameplay features:
/game-creator:add-feature [describe what you want]- Upgrade to pixel art (if using shapes):
/game-creator:add-assets- Re-record promo video:
/game-creator:record-promo- Launch a playcoin for your game (token rewards for players)
- Keep iterating! Run any step again:
,/game-creator:design-game/game-creator:add-audio- Redeploy after changes:
npm run deploy