Build bite-sized games the whole jar can't put down.
Ship an HTML5 or Godot game, wire up two tiny hooks, and you're live in the feed β and in tournaments β in seconds. Here's everything you need. π
Two ways into the jar
HTML5 games
A single folder with an index.html. Canvas, Phaser, PixiJS, Three.js β if it runs in a mobile browser, it runs here.
Godot games
Export your Godot 4 project to HTML5, drop in the same folder, and talk to CookieJar through the JavaScript bridge.
From zero to playable in 4 steps
- 1
Create your game folder
Every game lives in its own folder. The folder name becomes the game id and its URL.
folder structurepublic/ βββ games/ βββ my-game/ β the folder name IS your game id βββ index.html β entry point (required) βββ game.js β (optional) your code βββ assets/ β (optional) images, audio, fonts # Live at: games.thecookiejar.app/my-game - 2
Add the CookieJar bridge
Pause when the player swipes away, resume when they come back. Paste this once into your game.
cookiejar-bridge.js// Pause when the player swipes away, resume on return. let paused = false; // From the native WebView shell (postMessage) window.addEventListener('message', (e) => { if (!e.data?.cookiejar) return; if (e.data.cookiejar === 'pause') paused = true; // game.pause() if (e.data.cookiejar === 'resume') paused = false; // game.resume() }); // From the in-browser inject (custom events) window.addEventListener('cookiejar:pause', () => { paused = true; }); window.addEventListener('cookiejar:resume', () => { paused = false; }); - 3
Report your score
Call gameOver(score) the instant a run ends so it counts toward tournaments.
report-score.js// Call ONCE per run, the moment the run ends (game over / out of lives). function reportScore(score) { score = Math.max(0, Math.floor(Number(score) || 0)); // clean integer if (window.CookieJar?.gameOver) { window.CookieJar.gameOver(score); // preferred } else if (window.CookieJar?.submitScore) { window.CookieJar.submitScore(score); // fallback } // No CookieJar object? You're running outside the app β that's fine, // your game still works standalone in the browser. } function endGame() { // ...show your game-over screen... reportScore(currentScore); } - 4
Submit & publish
Send us your game folder. Once it's in, it's instantly live at
games.thecookiejar.app/your-game-idand starts surfacing in the feed. π
The CookieJar bridge
The bridge is how your game and the app talk. It's two small contracts β and both are optional safety nets, so your game always works on its own too.
βΈοΈ Pause & resume
CookieJar tells your game when it scrolls in and out of view. Listen for both the message and cookiejar:pause / cookiejar:resume events so you behave in the native app and the browser.
π― Score reporting
Call window.CookieJar.gameOver(score) exactly once per run. Always guard the call (window.CookieJar?.gameOver) and send a clean non-negative integer.
Make your game competitive
Tournaments turn any game into a leaderboard race. You don't build the leaderboard β CookieJar does. You just report an honest score and the platform handles ranking, timing, and prizes.
Run ends
Player loses, runs out of lives, or finishes a round.
You report
Your game calls gameOver(score) once, with a single integer.
We rank
CookieJar logs it against the live tournament and updates the board.
// Call ONCE per run, the moment the run ends (game over / out of lives).
function reportScore(score) {
score = Math.max(0, Math.floor(Number(score) || 0)); // clean integer
if (window.CookieJar?.gameOver) {
window.CookieJar.gameOver(score); // preferred
} else if (window.CookieJar?.submitScore) {
window.CookieJar.submitScore(score); // fallback
}
// No CookieJar object? You're running outside the app β that's fine,
// your game still works standalone in the browser.
}
function endGame() {
// ...show your game-over screen...
reportScore(currentScore);
}β οΈ Fair play: send the score only when the run is genuinely over, never mid-run, and never more than once. Clamp negatives to zero and round to a whole number so every board stays clean.
Shipping a Godot game
Export your project to HTML5 (Project β Export β Web), then reach the bridge through JavaScriptBridge β Godot's window into the page's JavaScript.
# Godot 4 β call the JS bridge from GDScript after exporting to HTML5.
func _report_score(score: int) -> void:
if OS.has_feature("web"):
JavaScriptBridge.eval("""
(function(s){
if (window.CookieJar && window.CookieJar.gameOver)
window.CookieJar.gameOver(Math.max(0, Math.floor(s)));
})(%d);
""" % score)
# Pause / resume β listen for CookieJar events and toggle the tree.
func _ready() -> void:
if OS.has_feature("web"):
var cb := JavaScriptBridge.create_callback(_on_cookiejar)
JavaScriptBridge.eval("window.__cjPause = null;")
# bind cb to window events in your HTML shell, then:
pass
func _on_cookiejar(args) -> void:
get_tree().paused = (args[0] == "pause")Tip: wrap every bridge call in OS.has_feature("web") so your desktop test builds don't choke on the missing browser APIs.
What makes a great jar game
Portrait & touch-first
Design for a tall mobile viewport and one-handed play. Scale your canvas to the screen and support touch.
Instant to start
Bite-sized means fast. Keep the folder light, lazy-load heavy assets, and get to the first frame quickly.
Endless replay
Quick runs that beg for 'one more go'. A clear score and a fast restart loop keep players in the feed.
Self-contained
Everything in one folder with relative paths. No external build server, no cross-origin dependencies.
Ready to drop your game in the jar?
Send us your game folder and we'll get it live in the feed. Got a question first? We're happy to help.
Developer FAQ
The questions we get most from game makers.
Still stuck? Email us