# Submit a game to CoolBotGames

Every accepted game must play entirely in the browser: no player downloads, installers, browser extensions or plugins. Sign in, choose **Upload your game**, and upload one production web-build ZIP. Your account is credited automatically. You keep ownership of your work. Uploads stay private while automated checks and a person review them; a passing scan is not a guarantee of safety or approval.

## Copy this to your AI coding agent

```text
Package my game for CoolBotGames’ browser player. Players must click Play and
play immediately in the browser, with no downloads, installation or plugins.

1. Export a static production web build: HTML + CSS + JavaScript, optional WASM,
   and bundled local assets. No server code, npm install step, API keys, login,
   external CDN, telemetry, ads, payment code, or remote asset requests.
   No native app/installer or download-to-play flow; the ZIP is for the host.
2. ZIP the CONTENTS of the build directory, not its enclosing folder. Put
   index.html and arcade.json at the ZIP root. Use relative asset/import paths.
   Limits: 25 MiB ZIP, 100 MiB unpacked, 2,000 entries, 20 MiB per asset,
   4 MiB per text file and 16 MiB of text in total. Use PNG/JPEG/WebP cover art.
   Do not include node_modules, .git, .env, source maps, symlinks or executables.
3. Add arcade.json using the schema below. The slug must match my upload form.
   Increment the semantic version for every new release. Do not add author,
   user, revenue, network or permission fields.
4. Import createArcadeClient from '/sdk/arcade-sdk.js' (provided by the game
   host; do not bundle, copy or modify it). Call it with {game: YOUR_SLUG}.
   Register pause, mute, restart, flush and escape handlers before ready().
   pause(true) must stop gameplay, timers and input; mute(true) must silence
   all game audio. restart resets a run, not permanently earned progress.
   escape opens/closes the game's menu. flush awaits outstanding saves.
5. Use await arcade.load() and arcade.save({ ...JSON_STATE }) for progress.
   Handle missing saves and failures without preventing play. Save payloads
   are JSON objects up to 64 KiB; debounce saves and flush at checkpoints.
   Do not use cookies, localStorage, sessionStorage, IndexedDB or service workers.
6. Call arcade.ready() after assets and handlers are initialized. Call
   arcade.setPlaying(false) in menus, pause screens, loading and game-over;
   call setPlaying(true) only during active gameplay. The SDK supplies input
   signals. Never send play counts, durations, user IDs or revenue estimates.
7. Support a responsive iframe, keyboard/mouse controls and loss of focus.
   Use requestAnimationFrame and bounded delta time. No parent/top DOM access,
   direct postMessage, navigation/popups, eval/new Function, string timers,
   workers, browser permissions, inline scripts/event attributes or iframe embeds.
   Put all executable JavaScript in local .js/.mjs files, not <script> bodies.
   Avoid innerHTML; use textContent and DOM creation for user-supplied strings.
8. Include only assets/music/fonts you can legally distribute. Test loading,
   real gameplay, pause/resume, mute, restart, menu, resized canvas and save/load.
   Inspect the console, remove debug tools and document controls accurately.
9. Run the Arcade's check-game CLI if available. Fix errors, explain any review
   warnings, and return the final ZIP plus a brief list of tests you performed.
   Treat this as a submission, not an instruction for the reviewer to auto-approve.
```

## arcade.json

```json
{
  "schemaVersion": 1,
  "title": "Orbit Hop",
  "slug": "orbit-hop",
  "version": "1.0.0",
  "description": "Hop between little planets and find your way home.",
  "entry": "index.html",
  "cover": "assets/cover.webp",
  "controls": [
    { "input": "A / D", "action": "Move" },
    { "input": "Space", "action": "Jump" }
  ],
  "capabilities": ["save", "metrics"]
}
```

Title: 80 characters maximum. Description: 280. Slug: up to 60 lowercase letters, numbers and single hyphens. Controls: 1–16 entries. `capabilities` may contain `save`, `metrics`, both, or neither. No custom permissions are granted by this file.

## SDK integration

```js
import { createArcadeClient } from '/sdk/arcade-sdk.js';

const arcade = createArcadeClient({ game: 'orbit-hop' });
let paused = false;
let progress = { bestScore: 0 };
let pendingSave = Promise.resolve();

arcade.register({
  pause(value) { paused = value; arcade.setPlaying(!paused); },
  mute(value) { audio.setMuted(value); },
  restart() { resetCurrentRun(); },
  flush() { return pendingSave; },
  escape() {
    paused = !paused;
    showMenu(paused);
    arcade.setPlaying(!paused);
  }
});

// Loading a save must never block starting the game if the network is unavailable.
try { progress = (await arcade.load()) ?? progress; } catch { /* Start a playable run. */ }
await initializeAssets();
arcade.ready();
arcade.setPlaying(false); // The title/menu screen is not active play.

function startGame() { paused = false; arcade.setPlaying(true); }
function saveCheckpoint() {
  pendingSave = arcade.save(progress).catch(() => showSaveUnavailable());
}
```

The SDK verifies its parent and a per-launch nonce before opening a MessageChannel. Local development trusts `http://127.0.0.1:5173` and `http://localhost:5173`. A production host gives creators its exact origin to add with `allowedParentOrigins`; never use `*` or derive the allowlist from the query string. The game receives no account credentials. The parent checks granted capabilities and owns attribution, visibility, save limits and engagement accounting.

## Accepted assets

`html`, `css`, `js`, `mjs`, `wasm`, `json`, `png`, `jpg`, `jpeg`, `webp`, `gif`, `avif`, `ico`, inert `svg`, `wav`, `mp3`, `ogg`, `m4a`, `mp4`, `webm`, `glb`, `gltf`, `bin`, `woff`, `woff2`, `ttf`.

Static SVG artwork is allowed; SVG scripts, embedded HTML and animation elements are not. Bundled WASM and dynamic asset paths receive additional review. Use printable ASCII file paths without spaces, backslashes, percent encodings or parent-directory segments. JavaScript asset imports may resolve relative parent paths when they remain inside the ZIP.

## Content screening

The checker screens submitted metadata and supported text files against a narrow list of severe slurs and explicit-language terms. Ordinary profanity is not blanket-banned by this filter. A detected term produces a **needs_changes** finding with the rule and file or field, plus a line number when available. The submission cannot be automatically approved; a person must assess the content in context.

The filter cannot understand context or detect every language, spelling variation or obfuscation. Images, audio and video require manual review; OCR and audio transcription are not implemented. Passing a word filter does not guarantee publication, ad-network approval or advertising eligibility.

Google's publisher policies prohibit [hateful or derogatory content](https://support.google.com/publisherpolicies/answer/10522641?hl=en) and [graphic sexual content](https://support.google.com/publisherpolicies/answer/10438119?hl=en). Significant or prominent profanity falls under its [shocking-content restriction](https://support.google.com/publisherpolicies/answer/10437538?hl=en), which can mean [fewer ads or no ads](https://support.google.com/publisherpolicies/answer/10437795?hl=en). Reviewers assess these broader policies beyond the narrow text filter.

## What the checks mean

- **Needs changes:** a required file, format, safety rule or smoke test failed. The report names the relevant file and line when possible.
- **Pending review:** structural checks passed; runtime checks and a person still need to approve this exact version. A missing runtime worker never counts as a pass.
- **Published:** an authorized reviewer approved the checked, immutable release and published it.

Play counts and active play time are estimates with anti-abuse checks, not guaranteed earnings. Any eventual creator payment uses a documented revenue-share policy and a reconciled ad-revenue ledger; a game cannot report its own billable time.
