Help us improve
Share bugs, ideas, or general feedback.
From game-creator
Registers browser games on Play.fun (OpenGameProtocol), integrates SDK for points tracking/leaderboards/wallet, and generates monetized play.fun URLs. For deployed Phaser/Three.js projects.
npx claudepluginhub playableintelligence/game-creator --plugin game-creatorHow this skill is triggered — by the user, by Claude, or both
Slash command
/game-creator:monetize-game [game-path][game-path]The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Register your game on [Play.fun](https://play.fun) (OpenGameProtocol), integrate the browser SDK for points tracking and leaderboards, and get a shareable play.fun URL. This is the link you post to Moltbook.
Turns tweets, stories, or prompts into scaffolded, designed, deployed, monetized browser games using Phaser/Three.js in one session. For quick viral prototypes.
Integrates Playgama Bridge SDK into Godot 3/4 games for HTML5 cross-platform publishing to Playgama, YouTube, Yandex, with ads, IAP, leaderboards, social features.
Guides integration of Playgama Bridge SDK into Unity/C# for WebGL game publishing across 20+ platforms with ads, IAP, leaderboards, social features.
Share bugs, ideas, or general feedback.
Register your game on Play.fun (OpenGameProtocol), integrate the browser SDK for points tracking and leaderboards, and get a shareable play.fun URL. This is the link you post to Moltbook.
What you'll get:
/game-creator:viral-game first for a one-shot deploy, or ensure your game is deployed to here.now / GitHub Pages / Vercel / etc.)Parse $ARGUMENTS to find the game project directory. If not provided, check the current working directory for a package.json with Phaser or Three.js dependencies.
Read package.json to confirm this is a game project. Read vite.config.js or similar to determine the deployed URL (look for base path).
Check if the user already has Play.fun credentials:
node skills/playdotfun/scripts/playfun-auth.js status
If credentials exist and are valid, skip to Step 2.
If no credentials, walk the user through authentication:
To register your game on Play.fun, you need to authenticate first.
I'll start a local auth server. Click the link below to log in:
node skills/playdotfun/scripts/playfun-auth.js callback &
Then tell the user:
Open this URL in your browser: https://app.play.fun/skills-auth?callback=http://localhost:9876/callback
Log in with your Play.fun account. The credentials will be saved automatically. Tell me when you're done.
Wait for user confirmation. Then verify:
node skills/playdotfun/scripts/playfun-auth.js status
If verification fails, offer the manual method as a fallback:
If the callback didn't work, you can paste your credentials manually. Go to your Play.fun dashboard, copy your API credentials (base64 format), and I'll save them:
node skills/playdotfun/scripts/playfun-auth.js manual <your-base64-credentials>
Find the deployed game URL. Check in this order:
Check for a here.now deploy state file:
cat .herenow/state.json 2>/dev/null | jq -r '.publishes | to_entries[0].value.siteUrl // empty'
If found, the URL is the siteUrl value (e.g., https://<slug>.here.now/)
Look for a GitHub Pages URL by running:
GITHUB_USER=$(gh api user --jq '.login' 2>/dev/null)
REPO_NAME=$(basename $(git remote get-url origin 2>/dev/null) .git 2>/dev/null)
If both resolve, the URL is https://$GITHUB_USER.github.io/$REPO_NAME/
Check vite.config.js for a base path that hints at the deployment URL
Ask the user for their game URL if it can't be determined
Verify the URL is accessible:
curl -s -o /dev/null -w "%{http_code}" "$GAME_URL"
Read the game's package.json for the name and description. Read src/core/Constants.js (or equivalent) to determine reasonable anti-cheat limits based on the scoring system.
Use the Play.fun MCP register_game tool if available. Otherwise, use the API directly:
Load the playdotfun skill for API reference. Register the game via POST https://api.play.fun/games with:
{
"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 (from the playdotfun skill):
maxScorePerSession: 100-500maxScorePerSession: 500-2000maxScorePerSession: 1000-5000Save the returned game UUID — you'll need it for the SDK integration.
Tell the user:
Your game is registered on Play.fun! Game ID:
<uuid>Name:<name>
Integrate the SDK into the game. This is a lightweight addition — the SDK loads from CDN and provides a points widget overlay.
index.htmlRetrieve the user's Play.fun public API key from stored credentials:
node skills/playdotfun/scripts/playfun-auth.js get-key
The script prints only the public API key to stdout. If no key is found, prompt the user to authenticate first (Step 1).
Security note: The
x-ogp-keyis a public client identifier — analogous to a Stripe publishable key or Firebase API key. It is designed to be embedded in client-side HTML and does not grant write access, authentication, or any privileged operations. The secret key (used for server-side API calls) is never embedded in game files.
Then add before the closing </head> tag:
<meta name="x-ogp-key" content="<PUBLIC_API_KEY>" />
<script src="https://sdk.play.fun/latest"></script>
Important: The x-ogp-key meta tag must contain the user's Play.fun public API key (not the game ID or secret key). Do NOT leave the placeholder — always substitute the actual key from playfun-auth.js get-key.
src/playfun.js integration moduleCreate a module that wires the Play.fun SDK into the game's EventBus:
// src/playfun.js
// Play.fun (OpenGameProtocol) integration
// Wires game events to Play.fun points tracking
import { eventBus, Events } from './core/EventBus.js';
const GAME_ID = '<game-uuid>';
let sdk = null;
let initialized = false;
export async function initPlayFun() {
if (typeof OpenGameSDK === 'undefined' && typeof PlayFunSDK === 'undefined') {
console.warn('Play.fun SDK not loaded — skipping monetization');
return;
}
const SDKClass = typeof PlayFunSDK !== 'undefined' ? PlayFunSDK : OpenGameSDK;
sdk = new SDKClass({
gameId: GAME_ID,
ui: { usePointsWidget: true },
});
await sdk.init();
initialized = true;
console.log('Play.fun SDK initialized');
wireEvents();
}
function wireEvents() {
// addPoints() — call frequently during gameplay to buffer points locally (non-blocking)
if (Events.SCORE_CHANGED) {
eventBus.on(Events.SCORE_CHANGED, ({ score, delta }) => {
if (sdk && 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!
if (Events.GAME_OVER) {
eventBus.on(Events.GAME_OVER, () => {
if (sdk && initialized) {
sdk.savePoints(); // Uses buffered points from addPoints() calls
}
});
}
// Save on page unload (browser handles this gracefully)
window.addEventListener('beforeunload', () => {
if (sdk && 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).
Important: Read the actual EventBus.js to find the correct event names. Common patterns:
SCORE_CHANGED / score:changed with { score, delta } or { score }GAME_OVER / game:overAdapt the event names and payload destructuring to match what the game actually emits. If the game doesn't emit a delta, compute it by tracking the previous score.
Add the Play.fun init call to the game's entry point (src/main.js):
import { initPlayFun } from './playfun.js';
// After game initialization
initPlayFun().catch(err => console.warn('Play.fun init failed:', err));
The initPlayFun() call should be non-blocking — if the SDK fails to load (e.g., ad blocker), the game still works.
The Play.fun SDK automatically sets CSS custom properties on the game iframe's document.documentElement when running inside the dashboard:
--ogp-safe-top-inset — space below the Play.fun header bubbles (~68px on mobile)--ogp-safe-bottom-inset — space above Safari bottom controls (~148px on mobile)Both default to 0px when not in the dashboard. The game templates already read these values in Constants.js (via SAFE_ZONE.TOP / SAFE_ZONE.BOTTOM for Phaser, or CSS var() for Three.js HTML overlays).
Verify that all UI elements (score, buttons, touch controls) are positioned within the safe area — not clipped behind the Play.fun header or Safari bottom bar. If the game has custom HUD positioning, update it to reference SAFE_ZONE constants or the CSS variables.
cd <project-dir> && npm run build
If the build fails, fix the integration code and retry.
Then redeploy. Use npm run deploy if available, otherwise detect the platform:
here.now (default):
~/.agents/skills/here-now/scripts/publish.sh dist/
GitHub Pages (if project uses it):
npx gh-pages -d dist
Verify the deployment is live (here.now deploys are instant; GitHub Pages may take 1-2 minutes):
curl -s -o /dev/null -w "%{http_code}" "$GAME_URL"
Tell the user:
Your game is monetized on Play.fun!
Play your game:
<game-url>View on Play.fun:https://play.fun/games/<game-uuid>What's live:
- Points widget overlay (bottom-right corner)
- Leaderboard tracking
- Wallet connect for claiming rewards
- Points buffered during gameplay, saved on game over
Share on Moltbook: Post your game URL to moltbook.com — 770K+ agents on the agent internet ready to play and upvote.
Next steps:
- Launch a playcoin for your game (token rewards for players)
- Check your leaderboard on Play.fun
- Share the play.fun URL on social media
/monetize-game examples/flappy-bird
Result: Auth with Play.fun → registers game with anti-cheat limits (maxScorePerSession: 1000) → adds SDK to index.html + creates src/playfun.js → wires score events to addPoints(), game-over to savePoints() → rebuilds → redeploys → live at https://play.fun/games/<uuid>. Points widget visible in-game.
Cause: Pop-up blockers or strict CORS policies prevent the localhost:9876 OAuth callback from completing.
Fix: Try the manual paste flow instead — run the auth script with --manual flag, copy the token from the dashboard at https://play.fun/dashboard, and paste it when prompted.
Cause: Missing required fields (name, description, or URL) or the game URL is not publicly accessible. Fix: Ensure the game is deployed and accessible at the provided URL before registering. Check that all required fields are filled in. If using here.now, verify the deployment hasn't expired.
Cause: Browser ad blockers and privacy extensions block the Play.fun SDK CDN (sdk.play.fun).
Fix: The SDK integration should be non-blocking — if it fails to load, the game still works. Add a try/catch around SDK initialization and log a warning. Test with ad blockers disabled to verify SDK functionality.
Cause: The SDK's point-saving UI overlay triggers at unexpected times, interrupting the player.
Fix: Only call savePoints() during natural game pauses (game over screen, level complete). Never call it during active gameplay. Use the silent: true option if available to suppress the modal.
Cause: Credentials not configured or expired since last session.
Fix: Visit https://play.fun/dashboard to refresh creator credentials. Re-run the auth flow with playfun-auth.js or manually update the .env file with fresh PLAYFUN_API_KEY and PLAYFUN_SECRET_KEY values.
x-ogp-key meta tag contains a public client identifier, not a secret. It is equivalent to a Stripe publishable key or Firebase web API key — designed for client-side use, safe to deploy publicly. It cannot be used to modify game data, access user accounts, or perform privileged operations.register_game API call and is never written to game source files or deployed artifacts.playfun-auth.js in a local credential store. The agent never reads internal agent configuration files (e.g., ~/.claude.json).