Developers

Read this page as markdown — for coding agents, or for anyone who prefers the source.

Publishing guide

How to get a game onto Bananai. Read SUBMISSION-RULES.md first — this document covers the mechanics, that one covers the requirements.

Everything below is yours to do. Registering the game in the catalogue and deploying it are ours, and happen after review; you never edit a file outside your own folder.

The fastest start is not this page. It is the starter: a complete working game whose sizing, SDK lifecycle, storage guards and touch handling are already correct. Copy it, change the id, replace the game.


1. Create the folder

<id>/
    ├── bananai.json
    ├── index.html
    ├── assets/thumb.png
    ├── css/style.css
    └── js/
        ├── main.js        bootstrap: mounts the game, wires the SDK
        └── <id>.js        game logic
    

One top-level directory, named as the game id, with everything inside it. It is published at /games/<id>/ unchanged — the folder you send is the folder that ships.

The <id> is lowercase, hyphen-separated, and permanent — it appears in URLs, cache keys and score records. It must match id in bananai.json and the directory name exactly. A submission arrived once as pallanoids against a manifest saying pallanoid, and it had to be corrected by hand.

2. Load the SDK

<script src="/sdk/v0/bananai-sdk.js"></script>
    

Root-relative, with no origin in front of it. Your game is served from the portal's own origin, so this always resolves; https://bananai.games/sdk/v0/bananai-sdk.js is a cross-origin request everywhere except live production and is rejected by the checks. You may also vendor a copy into your folder — ARCADE-SDK.md covers the trade-off.

await Bananai.init({ gameId: '<id>' });
    Bananai.loadingFinished();

    Bananai.gameplayStart();                  // play begins, and after every resume
    // ...
    Bananai.gameplayStop();                   // pause, settings, tab hidden
    // ...
    Bananai.gameOver({ score, level, durationMs });   // implies gameplayStop()

    window.addEventListener('bananai:restart', () => resetGame());
    

gameplayStop() is not optional — it is in the MUST list of the submission rules, and the pair is what measures the playtime you are paid on. A game that starts play and never stops it reports one session that runs until the tab closes, which is not a favour to anybody.

Full reference: ARCADE-SDK.md. A complete working game, runnable as it stands: the starter.

3. Technical requirements

4. Thumbnail

assets/thumb.png inside your folder (or .svg, or .webp), square, 400×400, declared in bananai.json.

It lives inside the game folder, not in the portal's shared /assets/. The submission is then one self-contained directory that can be added or removed as a unit — no orphaned image left behind when a game is withdrawn.

It has to read clearly at roughly 180px, which is the actual card size in the grid. Check it at that size, not at full resolution.

Compose it; do not screenshot the starting screen. No HUD — no score, no level, no press SPACE line: the card prints the title and category beside the image already, and at 180px that text is a smear. One subject filling the frame, built from few large shapes. Putting the game's name inside the image is allowed and usually worth it. The full reasoning, and what went wrong when this said only "400×400, legible at 180px", is in SUBMISSION-RULES.md.

Strongly recommended: an animated hover thumbnail. Optional, but fourteen of the fifteen games in the catalogue have one, and on desktop a card that does not move under the cursor looks unfinished beside them. Put it at assets/thumb-hover.webp and declare it as thumbHover in bananai.json, alongside thumb. It must be an animated WebP, not a GIF, not an APNG and not a video: 400×400, 2–3 seconds looping seamlessly, under 300 KB, and the same composition rules as the still image. Details in SUBMISSION-RULES.md.

5. Send it

Zip the folder — the whole thing, with your game id as the single top-level directory — and email it to hello@bananai.games. Under 25 MB compressed, which is the hard ceiling in the submission rules.

Tell us the game id, and what changed if this is not the first version.

An upload form will replace the email. The package it accepts is the same one described here, so nothing you build now is wasted.

What happens next

  1. Review. The game is loaded standalone and inside the shell at several viewport sizes, and played through the QA checklist — which is published precisely so you can run it first.
  2. Findings, if any, come back to you. They do not get fixed for you: your folder is yours, and nothing outside it may modify what is inside it. That is a rule, not a courtesy — a change we made to your game is a change you never agreed to and cannot maintain.
  3. We register it and deploy. The catalogue entry, the sitemap and the changelog are ours to write. The folder ships exactly as you sent it.

Before you send it

Everything here is something you can check yourself, on your own machine.

To serve it locally:

python -m http.server 8000 --directory <the folder containing your game>
    

Common failures lists the ways submissions are actually rejected, with the code that causes each one. It is a shorter read than this page and catches more.


Document history