make-game
npx skills add https://github.com/opusgamelabs/game-creator --skill make-game
Agent 安装分布
Skill 文档
Make Game (Full Pipeline)
Build a complete browser game from scratch, step by step. This command walks you through the entire pipeline â from an empty folder to a deployed, monetized game. No game development experience needed.
What you’ll get:
- A fully scaffolded game project with clean architecture
- Pixel art sprites â recognizable characters, enemies, and items (optional, replaces geometric shapes)
- Visual polish â gradients, particles, transitions, juice
- A 50 FPS promo video â autonomous gameplay capture, mobile portrait, ready for social media
- Chiptune music and retro sound effects (no audio files needed)
- Live deployment to here.now with an instant public URL
- Monetization via Play.fun â points tracking, leaderboards, wallet connect, and a play.fun URL to share on Moltbook
- Redeploy with a single command (
npm run deploy)
Quality assurance is built into every step â each code-modifying step runs build verification, visual review via Playwright MCP, and autofixes any issues found.
Orchestration Model
You are an orchestrator. You do NOT write game code directly. Your job is to:
- Set up the project (template copy, npm install, dev server)
- Create and track pipeline tasks using
TaskCreate/TaskUpdate - Delegate each code-writing step to a
Tasksubagent - Run the Verification Protocol (build + visual review + autofix) after each code-modifying step
- Report results to the user between steps
What stays in the main thread:
- Step 0: Parse arguments, create todo list
- Step 1 (infrastructure only): Copy template, npm install, playwright install, start dev server
- Verification protocol orchestration (launch QA subagent, read text result, launch autofix if needed)
- Step 4 (deploy): Interactive auth requires user back-and-forth
What goes to subagents (via Task tool):
- Step 1 (game implementation): Transform template into the actual game concept
- Step 1.5: Pixel art sprites and backgrounds
- Step 2: Visual polish
- Step 2.5: Promo video capture
- Step 3: Audio integration
Each subagent receives: step instructions, relevant skill name, project path, engine type, dev server port, and game concept description.
Verification Protocol
Run this protocol after every code-modifying step (Steps 1, 1.5, 2, 3). Step 2.5 (Promo Video) does not modify game code, so it skips QA. It delegates all QA work to a subagent to minimize main-thread context usage.
Playwright MCP Check (once, before first QA run)
Before the first QA run (after Step 1 infrastructure setup), check if Playwright MCP tools like browser_navigate are available. If not:
- Run:
claude mcp add playwright npx @playwright/mcp@latest - Tell the user: “Playwright MCP has been added. Please restart Claude Code for it to take effect, then tell me to continue.”
- Wait for user to restart and confirm. Do not proceed until MCP tools are available.
QA Subagent
Launch a Task subagent with these instructions:
You are the QA subagent for the game creation pipeline.
Project path:
<project-dir>Dev server port:<port>Step being verified:<step name>Run these phases in order. Stop early if a phase fails critically (build or runtime).
Phase 1 â Build Check
cd <project-dir> && npm run buildIf the build fails, report FAIL immediately with the error output.
Phase 2 â Runtime Check
cd <project-dir> && node scripts/verify-runtime.mjsIf the runtime check fails, report FAIL immediately with the error details.
Phase 3 â Gameplay Verification
cd <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 (
output/iterate/state-*.json) and error files (output/iterate/errors-*.json):
- Scoring: At least one state file should show
score > 0- Death: At least one state file should show
mode: "game_over". 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.- Errors: No critical errors in error files
Skip this phase if
scripts/iterate-client.jsis not present.Phase 4 â Architecture Validation
cd <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:
browser_navigatetohttp://localhost:<port>browser_wait_forâ wait 2 seconds for the game to loadbrowser_take_screenshotâ save asoutput/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),
browser_take_screenshotâ save asoutput/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: If
browser_take_screenshothangs 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.Note on iterate screenshots: The iterate-client uses
canvas.toDataURL()which returns blank/black images when Phaser uses WebGL withpreserveDrawingBuffer: false. Always prefer Playwright MCP viewport screenshots (browser_take_screenshot) over iterate screenshots for visual review.Without MCP (fallback):
- Read the iterate screenshots from
output/iterate/shot-*.png(may be black if WebGL â this is expected)- 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
Orchestrator Flow
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 step
Autofix Logic
When the QA subagent reports FAIL:
- Read
output/autofix-history.jsonto see what fixes were already attempted. If a previous entry matches the sameissueandfix_attemptedwithresult: "failure", instruct the subagent to try a different approach. - Launch a fix subagent via
Tasktool with:- The ISSUES list from the QA result
- The phase that failed (build errors, runtime errors, gameplay issues, visual problems)
- Any relevant failed attempts from
output/autofix-history.jsonso the subagent knows what NOT to repeat
- After each autofix attempt, append an entry to
output/autofix-history.json:{ "step": "<step name>", "issue": "<what failed>", "fix_attempted": "<what was tried>", "result": "success|failure", "timestamp": "<ISO date>" } - Re-run the QA subagent (all phases)
- Up to 3 total attempts per step (1 original + 2 retries)
- If all 3 attempts fail, report the failure to the user and ask whether to skip or abort
Important: Always fix issues before proceeding to the next step. The autofix loop ensures each step produces working, visually correct output.
Instructions
Step 0: Initialize pipeline
Parse $ARGUMENTS to determine the game concept. Arguments can take two forms:
Form A: Direct specification
- Engine:
2d(Phaser â side-scrollers, platformers, arcade) or3d(Three.js â first-person, third-person, open world). If not specified, ask the user. - Name: The game name in kebab-case. If not specified, ask the user what kind of game they want and suggest a name.
Form B: Tweet URL as game concept
If $ARGUMENTS contains a tweet URL (matching x.com/*/status/*, twitter.com/*/status/*, fxtwitter.com/*/status/*, or vxtwitter.com/*/status/*):
- Fetch the tweet using the
fetch-tweetskill â convert the URL tohttps://api.fxtwitter.com/<user>/status/<id>and fetch withWebFetch - Default to 2D (Phaser) â tweets describe ideas that map naturally to 2D arcade/casual games
- Creatively abstract a game concept from the tweet text. Your job is creative transformation â extract themes, dynamics, settings, or mechanics and reinterpret them as a game. NEVER refuse to make a game from a tweet. Every tweet contains something that can inspire a game:
- News about weather â survival game, storm-dodging game
- Sports result â arcade sports game
- Political/legal news â strategy game, puzzle game, tower defense
- Personal story â narrative adventure, platformer themed around the journey
- Product announcement â tycoon game, builder game
- Abstract thought â puzzle game, experimental art game
- The transformation is the creative act. You are not recreating or trivializing the source â you are using it as a springboard for an original game concept.
- Generate a game name in kebab-case from the abstracted concept (not from literal tweet content)
- Tell the user what you extracted:
Found 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?
Wait for user confirmation before proceeding. The user can override the engine (to 3D) or the name at this point.
Celebrity Detection:
After determining the game concept, scan the concept description, tweet text, and any mentioned people for celebrity/public figure names. Check against:
character-library/manifest.json(relative to plugin root) â exact slug match or name match- Common name recognition â politicians, tech CEOs, world leaders, entertainers
If celebrities are detected:
- Set
hasCelebrities = trueand list detected names - Note in
progress.mdwhich characters are pre-built vs need building - 2D: The Step 1.5 subagent will use photo-composite characters for these
- 3D: For each celebrity, try: (1) generate with Meshy AI â
"a cartoon caricature of <Name>, <distinguishing features>, low poly game character"then rig for animation, (2) check3d-character-library/manifest.jsonfor a pre-built match, (3) search Sketchfab withfind-3d-asset.mjs, (4) fall back to best-matching library model. Meshy generation produces the best results for named personalities since it can capture specific visual features.
Meshy API Key (3D games only):
If the engine is 3D, check if MESHY_API_KEY is set in the environment. If not, ask the user immediately in Step 0 â don’t wait until Step 1.5:
I’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)
Store the key for all subsequent meshy-generate.mjs calls throughout the pipeline.
Create all pipeline tasks upfront using TaskCreate:
- Scaffold game from template
- Add assets: pixel art sprites (2D) or Meshy AI-generated GLB models + animated characters (3D)
- Add visual polish (particles, transitions, juice)
- Record promo video (autonomous 50 FPS capture)
- Add audio (BGM + SFX)
- Deploy to here.now
- Monetize with Play.fun (register on OpenGameProtocol, add SDK, redeploy)
This gives the user full visibility into pipeline progress at all times. Quality assurance (build, runtime, visual review, autofix) is built into each step, not a separate task.
After creating tasks, create the output/ directory in the project root and initialize output/autofix-history.json as an empty array []. This file tracks all autofix attempts across the pipeline so fix subagents avoid repeating failed approaches.
Step 1: Scaffold the game
Mark task 1 as in_progress.
Main thread â infrastructure setup:
- Locate the plugin’s template directory. Check these paths in order until found:
- The agent’s plugin cache (e.g.
~/.claude/plugins/cache/local-plugins/game-creator/1.0.0/templates/) - The
templates/directory relative to this plugin’s install location
- The agent’s plugin cache (e.g.
- Determine the target directory. If the current working directory is the
game-creatorplugin repository (check forCLAUDE.mdmentioning “game-creator” or.claude-plugin/plugin.json), create the game insideexamples/(e.g.,examples/<game-name>/). Otherwise, create it in the current working directory (<game-name>/). - Copy the entire template directory to the target:
- 2D: copy
templates/phaser-2d/â<target-dir>/ - 3D: copy
templates/threejs-3d/â<target-dir>/
- 2D: copy
- Update
package.jsonâ set"name"to the game name - Update
<title>inindex.htmlto a human-readable version of the game name - Verify Node.js/npm availability: Run
node --version && npm --versionto confirm Node.js and npm are installed and accessible. If they fail (e.g., nvm lazy-loading), try sourcing nvm:export NVM_DIR="$HOME/.nvm" && source "$NVM_DIR/nvm.sh"then retry. If Node.js is not installed at all, tell the user they need to install it before continuing. - Run
npm installin the new project directory - Install Playwright and Chromium â Playwright is required for runtime verification and the iterate loop:
- Check if Playwright is available:
npx playwright --version - If that fails, check
node_modules/.bin/playwright --version - If neither works, run
npm install -D @playwright/testexplicitly - Then install the browser binary:
npx playwright install chromium - Verify success; if it fails, warn and continue (build verification still works, but runtime/iterate checks will be skipped)
- Check if Playwright is available:
- Verify template scripts exist â The template ships with
scripts/verify-runtime.mjs,scripts/iterate-client.js, andscripts/example-actions.json. Confirm they are present. Theverifyanditeratenpm scripts are already inpackage.jsonfrom the template. - Start the dev server â Before running
npm run dev, check if the configured port (invite.config.js) is already in use:lsof -i :<port> -t. If occupied, updatevite.config.jsto use the next available port (try 3001, 3002, etc.). Then start the dev server in the background and confirm it responds. Keep it running throughout the pipeline. Note the actual port number â pass it toscripts/verify-runtime.mjsvia thePORTenv variable in subsequent runs.
Subagent â game implementation:
Launch a Task subagent with these instructions:
You are implementing Step 1 (Scaffold) of the game creation pipeline.
Project path:
<project-dir>Engine:<2d|3d>Game concept:<user's game description>Skill to load:phaser(2D) orthreejs-game(3D)Core 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
FORCE_PORTRAIT = truein Constants.js. This locks portrait layout on desktop (pillarboxed with black bars viaScale.FIT + CENTER_BOTH). Use fixed design dimensions (540Ã960), not conditional_isPortrait ? 540 : 960.- Visible touch indicators required: Always render semi-transparent arrow buttons (or direction indicators) on touch-capable devices. Use capability detection (
('ontouchstart' in window) || (navigator.maxTouchPoints > 0)), NOT OS detection (device.os.android || device.os.iOS). Enable pointer events (pointerdown/pointermove/pointerup) on ALL devices â never gate behindisMobile. UseTOUCHconstants from Constants.js for sizing.- Minimum 7â8% canvas width for collectibles/hazards: Items smaller than 7% of
GAME.WIDTHbecome unrecognizable blobs on phone screens. Size attacks at ~9%, power-ups at ~7%, player character at 12â15%.- Wire spectacle events: emit
SPECTACLE_ENTRANCEincreate(),SPECTACLE_ACTIONon every player input,SPECTACLE_HITon score/destroy,SPECTACLE_COMBOon consecutive hits (pass{ combo }),SPECTACLE_STREAKat milestones (5, 10, 25 â pass{ streak }),SPECTACLE_NEAR_MISSon close callsVisual 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
create(): player starts off-screen, tweens into position withBounce.easeOut, landing shake + particle burst- Add combo tracking to GameState:
combo(current streak, resets on miss),bestCombo(session high), both reset inreset()- Ensure restart is clean â test mentally that 3 restarts in a row would work identically
- Add
isMutedto GameState for mute supportCRITICAL â Preserve the button pattern:
- The template’s
GameOverScene.jscontains a workingcreateButton()helper (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.Character & entity sizing:
- Character WIDTH from
GAME.WIDTH * ratio, HEIGHT fromWIDTH * SPRITE_ASPECT(whereconst SPRITE_ASPECT = 1.5for 200Ã300 spritesheets). Never define character HEIGHT asGAME.HEIGHT * ratioâ on mobile portrait,GAME.HEIGHTis much larger thanGAME.WIDTH, squishing characters.- For character-driven games (named personalities, mascots, famous figures): make the main character prominent â
GAME.WIDTH * 0.12toGAME.WIDTH * 0.15(12â15% of screen width). Use caricature proportions (large head = 40â50% of sprite height, exaggerate distinguishing features) for personality games.- Non-character entities (projectiles, collectibles, squares) can use
GAME.WIDTH * ratiofor both dimensions since they have no intrinsic aspect ratio to preserve.Play.fun safe zone:
- Import
SAFE_ZONEfromConstants.js. All UI text, buttons, and interactive elements (title text, score panels, restart buttons) must be positioned belowSAFE_ZONE.TOP. The Play.fun SDK renders a 75px widget bar at the top of the viewport (z-index 9999). UsesafeTop + usableH * ratiofor proportional positioning within the usable area (whereusableH = GAME.HEIGHT - SAFE_ZONE.TOP).Generate game-specific test actions: After implementing the core loop, overwrite
scripts/example-actions.jsonwith actions tailored to this game. Requirements:
- 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):
[ {"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):
[ {"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.
After subagent returns, run the Verification Protocol.
Create progress.md at the game’s project root. Read the game’s actual source files to populate it accurately:
- Read
src/core/EventBus.jsfor the event list - Read
src/core/Constants.jsfor the key sections (GAME, PLAYER, ENEMY, etc.) - List files in
src/entities/for entity names - Read
src/core/GameState.jsfor state fields
Write progress.md with this structure:
# 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]
Tell the user:
Your game is scaffolded and running! Here’s how it’s organized:
src/core/Constants.jsâ all game settings (speed, colors, sizes)src/core/EventBus.jsâ how parts of the game talk to each othersrc/core/GameState.jsâ tracks score, lives, etc.- 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.
Mark task 1 as completed.
Wait for user confirmation before proceeding.
Step 1.5: Add game assets
Always run this step for both 2D and 3D games. 2D games get pixel art sprites; 3D games get GLB models and animated characters.
Mark task 2 as in_progress.
Pre-step: Character Library Check
Before launching the asset subagent, check if the game uses personality characters. For each personality, resolve their sprites using this tiered fallback (try each tier in order, stop at the first success):
1. Read design-brief.md to identify personality characters and their slugs.
2. Resolve the character library path â find character-library/manifest.json relative to the plugin root:
- Check
character-library/manifest.jsonrelative to the plugin install directory - Check common plugin cache paths (e.g.,
~/.claude/plugins/cache/local-plugins/game-creator/*/character-library/)
3. For each personality, try these tiers in order:
Tier 1 â Pre-built (best): Check if slug exists in manifest.json. If yes, copy sprites:
mkdir -p <project-dir>/public/assets/characters/<slug>/
cp <plugin-root>/character-library/characters/<slug>/sprites/* \
<project-dir>/public/assets/characters/<slug>/
Result: 4-expression spritesheet ready. Done.
Tier 2 â Build from 4 images (good): WebSearch for 4 expression photos. Any photo format works (jpg, png, webp) â the pipeline has ML background removal built in, so transparent PNGs are NOT required. Search broadly:
- normal:
"<Name> portrait photo"or"<Name> face"â neutral expression - happy:
"<Name> smiling"or"<Name> laughing" - angry:
"<Name> angry"or"<Name> serious stern" - surprised:
"<Name> surprised"or"<Name> shocked"
Prefer real photographs (not illustrations/cartoons). Head shots and half-body shots both work â crop-head.mjs uses face detection to isolate the face automatically. Download as normal.jpg, happy.jpg, etc. (any image extension).
If all 4 found, download to <project-dir>/public/assets/characters/<slug>/raw/ and run:
node <plugin-root>/scripts/build-character.mjs "<Name>" \
<project-dir>/public/assets/characters/<slug>/ --skip-find
Result: 4-expression spritesheet. Done.
Tier 3 â Build from 1-3 images (acceptable): If WebSearch only finds 1-3 usable images:
- Download whatever was found to
raw/(e.g., onlynormal.pngandhappy.png) - Duplicate the best image (prefer normal) into the missing expression slots:
cp raw/normal.png raw/angry.png # fill missing with normal cp raw/normal.png raw/surprised.png - Run
build-character.mjsas above â all 4 raw slots are filled, pipeline produces a 4-frame spritesheet - Result: 4-frame spritesheet where some expressions share the same face. Functional â the expression system still works, just with less visual variety.
Tier 4 â Single image fallback (minimum): If WebSearch finds exactly 1 image OR the pipeline fails on some images:
- Use the single successful image for all 4 expression slots
- Run
build-character.mjsâ produces a spritesheet where all 4 frames are identical - Result: Character is recognizable but has no expression changes. Still photo-composite, still works with the expression wiring (just no visible change).
Tier 5 â Generative pixel art (worst case): If NO images can be found or the ENTIRE pipeline fails (bg removal crash, face detection fails on all images, network errors):
- Fall back to the Personality Character (Caricature) archetype from the
game-assetsskill â 32×48 pixel art grid at scale 4 - Note in
progress.md:"<Name>: pixel art fallback â no photo-composite available" - The subagent will create pixel art with recognizable features (hair, glasses, clothing) per the game-assets sprite design rules
- Result: No photo-composite, but the character is still visually distinct via pixel art caricature.
4. Record results for each character in 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)
5. Pass to subagent: the list of character slugs, which tier each resolved to, and how many unique expressions each has. The subagent needs this to know whether to wire full expression changes or skip expression logic for Tier 5 characters.
Launch a Task subagent with these instructions:
You are implementing Step 1.5 (Pixel Art Sprites) of the game creation pipeline.
Project path:
<project-dir>Engine: 2D (Phaser 3) Skill to load:game-assetsRead
progress.mdat the project root before starting. It describes the game’s entities, events, constants, and scoring system from Step 1.Character library sprites are already copied to
public/assets/characters/<slug>/. For personality characters, load the spritesheet and wire expression changes per the game-assets skill’s “Expression Wiring Pattern”. AddEXPRESSIONandEXPRESSION_HOLD_MSto Constants.js. Wire expression changes to EventBus events per the Expression Map indesign-brief.md.Follow the game-assets skill fully for non-personality entities:
- Read all entity files (
src/entities/) to findgenerateTexture()/fillCircle()calls- Choose the palette that matches the game’s theme (DARK, BRIGHT, or RETRO)
- Create
src/core/PixelRenderer.jsâ therenderPixelArt()+renderSpriteSheet()utilities- Create
src/sprites/palette.jswith the chosen palette- Create sprite data files (
player.js,enemies.js,items.js,projectiles.js) with pixel matrices- Create
src/sprites/tiles.jswith background tiles (ground variants, decorative elements)- 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 â 32×48 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 (16×16) or Small (12×12) 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
scene.add.text()being used as the primary identifier? If so, remove it and add physical features instead.- 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 a
## Step 1.5: Assetssection toprogress.mdwith: palette used, sprites created, any dimension changes to entities.Do NOT run builds â the orchestrator handles verification.
After 2D subagent returns, run the Verification Protocol.
3D Asset Flow (Three.js games)
For 3D games, generate custom models with Meshy AI and integrate them as animated characters and world props. This is the 3D parallel of the 2D pixel art step above.
Pre-step: Character & Asset Generation
The Meshy API key should already be obtained in Step 0. If not set, ask now (see Step 0 instructions).
-
Read
design-brief.mdto identify all characters (player + opponents/NPCs) and their names/descriptions. -
For EACH humanoid character, run the full generateârig pipeline as ONE atomic step:
Tier 1 â Generate + Rig with Meshy AI (preferred): This is a TWO-command chain â always run BOTH for humanoid characters. The rig step auto-downloads walk/run animation GLBs.
# 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>
After this completes you have 3 files per character:
<slug>.glbâ rigged model with skeleton (useloadAnimatedModel()+SkeletonUtils.clone())<slug>-walk.glbâ walking animation (auto-downloaded)<slug>-run.glbâ running animation (auto-downloaded)
NEVER generate humanoid characters without rigging. Static models require hacky programmatic animation that looks artificial.
For named personalities, be specific: "a cartoon caricature of Trump, blonde hair, suit, red tie, low poly game character, full body".
For multiple characters, generate each with a distinct description for visual variety. Run generateârig in parallel for different characters to save time.
Tier 2 â Pre-built in 3d-character-library/ (Meshy unavailable): Check manifest.json for a name/theme match. Copy the GLB:
cp <plugin-root>/3d-character-library/models/<model>.glb \
<project-dir>/public/assets/models/<slug>.glb
Tier 3 â Search Sketchfab: Use find-3d-asset.mjs to search for a matching animated model:
node <plugin-root>/scripts/find-3d-asset.mjs \
--query "<character name> animated character" \
--max-faces 10000 --list-only
Tier 4 â Generic library fallback: Use the best match from 3d-character-library/:
- Soldier â action/military/default human
- Xbot â sci-fi/tech/futuristic
- RobotExpressive â cartoon/casual/fun (most animations)
- Fox â nature/animal
When 2+ characters fall back to library, use different models to differentiate them.
3. Generate / search for world objects â Read design-brief.md entity list:
# 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/
4. Record results in 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)
Launch a Task subagent with these instructions:
You are implementing Step 1.5 (3D Assets) of the game creation pipeline.
Project path:
<project-dir>Engine: 3D (Three.js) Skill to load:game-3d-assetsandmeshyaiRead
progress.mdat the project root before starting. It lists generated/downloaded models and the character details.Rigged character GLBs + animation GLBs are already in
public/assets/models/. Set up the character controller:
- Create
src/level/AssetLoader.jsâ CRITICAL: useSkeletonUtils.clone()for rigged models (regular.clone()breaks skeleton bindings â T-pose). Import fromthree/addons/utils/SkeletonUtils.js.- Add
MODELSconfig toConstants.jswith:path(rigged GLB),walkPath,runPath,scale,rotationYper model. Start withrotationY: Math.PIâ most Meshy models face +Z and need flipping.- For each rigged model:
- Load with
loadAnimatedModel(), createAnimationMixer- 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
mixer.update(delta)every frame- Use
fadeToAction()pattern for smooth transitions- For static models (ring, props): use
loadModel()(regular clone)- 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
rotationYin Constants- 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
.catch()for every model loadAfter completing your work, append a
## Step 1.5: 3D Assetssection toprogress.mdwith: models used (Meshy-generated vs library), scale/orientation adjustments, verified facing directions.Do NOT run builds â the orchestrator handles verification.
After 3D subagent returns, run the Verification Protocol.
Tell the user (2D):
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:
src/core/PixelRenderer.jsâ rendering enginesrc/sprites/â all sprite data, palettes, and background tiles
Tell the user (3D):
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:
src/level/AssetLoader.jsâ model loader with SkeletonUtilspublic/assets/models/â Meshy-generated and/or library GLB models- OrbitControls camera with WASD movement
Next up: visual polish. I’ll add particles, screen transitions, and juice effects. Ready?
Mark task 2 as completed.
Wait for user confirmation before proceeding.
Step 2: Design the visuals
Mark task 3 as in_progress.
Launch a Task subagent with these instructions:
You are implementing Step 2 (Visual Design â Spectacle-First) of the game creation pipeline.
Project path:
<project-dir>Engine:<2d|3d>Skill to load:game-designerRead
progress.mdat the project root before starting. It describes the game’s entities, events, constants, and what previous steps have done.Apply 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:
cameras.main.flash(300)on scene start- Player slam-in: player starts off-screen, tweens in with
Bounce.easeOut, landing shake (0.012) + particle burst (20 particles)- 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
SPECTACLE_ACTIONandSPECTACLE_HIT- Floating score text (28px, scale 1.8,
Elastic.easeOut) onSCORE_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 a
## Step 2: Designsection toprogress.mdwith: improvements applied, new effects added, any color or layout changes.Do NOT run builds â the orchestrator handles verification.
After subagent returns, run the Verification Protocol.
Tell the user:
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.
Mark task 3 as completed.
Proceed directly to Step 2.5 â no user confirmation needed (promo video is non-destructive and fast).
Step 2.5: Record promo video
Mark task 4 as in_progress.
This step stays in the main thread. It does not modify game code â it records autonomous gameplay footage using Playwright and converts it with FFmpeg. No QA verification needed.
Pre-check: FFmpeg availability
ffmpeg -version | head -1
If FFmpeg is not found, warn the user and skip this step:
FFmpeg is not installed. Skipping promo video. Install it with
brew install ffmpeg(macOS) orapt install ffmpeg(Linux), then run/game-creator:promo-videolater.
Mark task 4 as completed and proceed to Step 3.
Copy the conversion script from the plugin:
cp <plugin-root>/skills/promo-video/scripts/convert-highfps.sh <project-dir>/scripts/
chmod +x <project-dir>/scripts/convert-highfps.sh
Launch a Task subagent to generate the game-specific capture script:
You are implementing Step 2.5 (Promo Video) of the game creation pipeline.
Project path:
<project-dir>Dev server port:<port>Skill to load:promo-videoRead
progress.mdand the following source files to understand the game:
src/scenes/GameScene.jsâ find the death/failure method(s) to patch outsrc/core/EventBus.jsâ understand event flowsrc/core/Constants.jsâ check input keys, game dimensionssrc/main.jsâ verify__GAME__and__GAME_STATE__are exposedCreate
scripts/capture-promo.mjsfollowing thepromo-videoskill template. You MUST adapt these game-specific parts:
Death patching â identify ALL code paths that lead to game over and monkey-patch them. Search for
triggerGameOver,gameOver,takeDamage,playerDied,onPlayerHit, or any method that setsgameState.gameOver = true. Patch every one.Input sequence â determine the actual input keys from the game’s input handling (look for
createCursorKeys(),addKeys(),input.on('pointerdown'), etc.). Generate agenerateInputSequence(totalMs)function that produces natural-looking gameplay for this specific game type:
- 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
{ width: 1080, height: 1920 }(9:16 mobile portrait) unless the game is desktop-only landscape.Duration â 13s of game-time by default. For slower-paced games (puzzle, strategy), use 8-10s.
Config: The script must accept
--port,--duration, and--output-dirCLI args with sensible defaults.Do NOT run the capture â just create the script. The orchestrator runs it.
After subagent returns, run the capture and conversion from the main thread:
# 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.5
Verify the output:
- Check
output/promo.mp4exists and is non-empty - Verify duration is approximately
DESIRED_GAME_DURATION / 1000seconds - Verify frame rate is 50 FPS
If capture fails (Playwright error, timeout, etc.), warn the user and skip â the promo video is a nice-to-have, not a blocker.
Extract a thumbnail for the user to preview:
ffmpeg -y -ss 5 -i output/promo.mp4 -frames:v 1 -update 1 output/promo-thumbnail.jpg
Read the thumbnail image and show it to the user.
Tell the user:
Promo video recorded! 50 FPS, mobile portrait (1080×1920).
File:
output/promo.mp4([duration]s, [size])This 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?
Mark task 4 as completed.
Wait for user confirmation before proceeding.
Step 3: Add audio
Mark task 5 as in_progress.
Launch a Task subagent with these instructions:
You are implementing Step 3 (Audio) of the game creation pipeline.
Project path:
<project-dir>Engine:<2d|3d>Skill to load:game-audioRead
progress.mdat the project root before starting. It describes the game’s entities, events, constants, and what previous steps have done.Apply the game-audio skill:
- Audit the game: check for
@strudel/web, read EventBus events, read all scenes- Install
@strudel/webif needed- Create
src/audio/AudioManager.js,music.js,sfx.js,AudioBridge.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(import { stack, note, s } from '@strudel/web') â do NOT rely on global registration- Mute toggle: Wire
AUDIO_TOGGLE_MUTEtogameState.game.isMuted. Add M key shortcut and a speaker icon UI button. See themute-buttonrule and the game-audio skill “Mute Button” section for requirements and drawing code.After completing your work, append a
## Step 3: Audiosection toprogress.mdwith: BGM patterns added, SFX event mappings, mute wiring confirmation.Do NOT run builds â the orchestrator handles verification.
After subagent returns, run the Verification Protocol.
Tell the user:
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 publish your game to here.now for an instant public URL. Ready?
Mark task 5 as completed.
Wait for user confirmation before proceeding.
Step 4: Deploy to here.now
Mark task 6 as in_progress.
Load the game-deploy skill. This step stays in the main thread because it may require user back-and-forth for API key setup.
6a. Check prerequisites
Verify the here-now skill is installed:
ls ~/.agents/skills/here-now/scripts/publish.sh
If not found, tell the user:
The here-now skill is needed for deployment. Install it with:
npx skills add heredotnow/skill --skill here-now -g -yTell me when you’re ready.
Wait for the user to confirm.
6b. Build the game
npm run build
Verify dist/ exists and contains index.html and assets. If the build fails, fix the errors before proceeding.
6c. Verify the Vite base path
Read vite.config.js. For here.now, the base should be '/' (the default). If it’s set to something else (e.g., a GitHub Pages subdirectory path), update it:
export default defineConfig({
base: '/',
// ... rest of config
});
Rebuild after changing the base path.
6d. Publish to here.now
~/.agents/skills/here-now/scripts/publish.sh dist/
The script outputs the live URL immediately (e.g., https://<slug>.here.now/).
Read and follow publish_result.* lines from script stderr. Save the slug for future updates.
If anonymous (no API key): The publish expires in 24 hours and will be permanently deleted unless the user claims it. The script returns a claim URL. You MUST immediately tell the user:
ACTION REQUIRED â your game will be deleted in 24 hours! Visit your claim URL to create a free here.now account and keep your game online permanently. The claim token is only shown once and cannot be recovered. Do this now before you forget!
Then proceed to 6e to help them set up permanent hosting.
If authenticated: The publish is permanent. Skip 6e.
6e. Set up permanent hosting
This step is strongly recommended for anonymous publishes. Help the user create a here.now account so their game stays online:
- Ask for their email
- Send magic link:
curl -sS https://here.now/api/auth/login -H "content-type: application/json" -d '{"email": "user@example.com"}' - Tell the user: “Check your inbox for a sign-in link from here.now. Click it, then copy your API key from the dashboard.”
- Save the key:
mkdir -p ~/.herenow && echo "<API_KEY>" > ~/.herenow/credentials && chmod 600 ~/.herenow/credentials - Re-publish to make it permanent:
~/.agents/skills/here-now/scripts/publish.sh dist/ --slug <slug>
6f. Verify the deployment
curl -s -o /dev/null -w "%{http_code}" "https://<slug>.here.now/"
Should return 200 immediately (here.now deploys are instant).
6g. Add deploy script
Add a deploy script to package.json so future deploys are one command:
{
"scripts": {
"deploy": "npm run build && ~/.agents/skills/here-now/scripts/publish.sh dist/"
}
}
Tell the user (if authenticated):
Your game is live!
URL:
https://<slug>.here.now/Redeploy after changes: Just run:
npm run deployOr if you’re working with me, I’ll rebuild and redeploy 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?
Tell the user (if anonymous â no API key):
Your game is live!
URL:
https://<slug>.here.now/IMPORTANT: Your game will be deleted in 24 hours unless you claim it! Visit your claim URL to create a free here.now account and keep your game online forever. The claim token is only shown once â save it now!
Redeploy after changes: Just run:
npm run deployNext 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?
Mark task 6 as completed.
Wait for user confirmation before proceeding.
Step 5: Monetize with Play.fun
Mark task 7 as in_progress.
This step stays in the main thread because it requires interactive authentication.
7a. Authenticate with Play.fun
Check if the user already has Play.fun credentials. The auth script is bundled with the plugin:
node skills/playdotfun/scripts/playfun-auth.js status
If credentials exist, skip to 7b.
If no credentials, start the auth callback server:
node skills/playdotfun/scripts/playfun-auth.js callback &
Tell the user:
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/callback
Log in with your Play.fun account. Credentials are saved locally. Tell me when you’re done.
Wait for user confirmation. Then verify with playfun-auth.js status.
If callback fails, offer manual method as fallback.
7b. Register the game on Play.fun
Determine the deployed game URL from Step 6 (e.g., https://<slug>.here.now/ or https://<username>.github.io/<game-name>/).
Read package.json for the game name and description. Read src/core/Constants.js to determine reasonable anti-cheat limits based on the scoring system.
Use the Play.fun API to register the game. Load the playdotfun skill for API reference. Register via POST 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>
}
Anti-cheat guidelines:
- Casual clicker/idle:
maxScorePerSession: 100-500 - Skill-based arcade (flappy bird, runners):
maxScorePerSession: 500-2000 - Competitive/complex:
maxScorePerSession: 1000-5000
Save the returned game UUID.
7c. Add the Play.fun Browser SDK
First, extract the user’s API key from stored credentials:
# 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"
If no API key is found, prompt the user to authenticate first.
Then add the SDK script and meta tag to index.html before </head>, substituting the actual API key:
<meta name="x-ogp-key" content="<USER_API_KEY>" />
<script src="https://sdk.play.fun/latest"></script>
Important: The x-ogp-key meta tag must contain the user’s Play.fun API key (not the game ID). Do NOT leave the placeholder â always substitute the actual key extracted above.
Create src/playfun.js that wires the game’s EventBus to Play.fun points tracking:
// 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(); });
}
Critical SDK behavior:
| Method | When to use | Behavior |
|---|---|---|
addPoints(n) |
During gameplay | Buffers points locally, non-blocking |
savePoints() |
Game over / level end | Opens blocking modal, syncs buffered points to server |
â ï¸ Do NOT call savePoints() on a timer or during active gameplay â it interrupts the player with a modal dialog. Only call at natural pause points (game over, level transitions, menu screens).
Read the actual EventBus.js to find the correct event names and payload shapes. Adapt accordingly.
Add initPlayFun() to src/main.js:
import { initPlayFun } from './playfun.js';
// After game init
initPlayFun().catch(err => console.warn('Play.fun init failed:', err));
7d. Rebuild and redeploy
cd <project-dir> && npm run build && ~/.agents/skills/here-now/scripts/publish.sh dist/
If the project was deployed to GitHub Pages instead, use npx gh-pages -d dist.
Verify the deployment is live (here.now deploys are instant; GitHub Pages may take 1-2 minutes).
7e. Tell the user
Your game is monetized on Play.fun!
Play:
<game-url>Play.fun: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.
Mark task 7 as completed.
Pipeline Complete!
Tell the user:
Your 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 here.now with an instant 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- Switch to GitHub Pages if you prefer git-based deploys:
/game-creator:game-deploy