# Bubble Pop: the prompt that made it This game was made by following the guide page "Make a game with AI" (/docs/make-with-ai) exactly: the starter prompt from the AI starter kit (`ai-starter/PROMPT.md`), with the [BRACKETS] filled in, sent as one message. The AI (Claude, playing the part of a free AI chat) answered with one index.html. That reply was saved as `index.html`, and `twiddleplay-sdk.js` from the kit was put next to it. No edits by hand. The exact prompt: ```text Make me a complete mobile game for Twiddle Play, an Android app that plays HTML5 games offline. I can't code, so give me finished code I can copy and save. Don't explain the code. MY GAME - Name: Bubble Pop - Idea: Colourful bubbles float up from the bottom of the screen, and the player taps them to pop them. Pops less than 1 second apart build a combo that multiplies the points (x2, x3, up to x10); tapping empty space breaks the combo. Rare golden bubbles are worth 5 times more, and bubbles get faster over time. If 5 bubbles float away off the top, the game is over. - Screen: portrait WHAT TO SEND BACK - ONE complete file named index.html, in ONE code block, with all the HTML, CSS and JavaScript inside it. - Always send the WHOLE file. Never write "..." or "the rest stays the same". - Keep it under about 800 lines, so it fits in one reply. - After the code block: 3 short lines on how to play, then a short list of things I should test. 1. OFFLINE ONLY - No http:// or https:// addresses anywhere in the file. No CDNs, no libraries, no online fonts, no images or sounds from the internet. - Draw everything with the canvas or CSS. Make sound effects with the Web Audio API (short beeps), not audio files. - Fonts: system-ui, sans-serif. - No fetch, no login, no accounts, no analytics, no other ad networks, no links, no window.open, and no alert, confirm or prompt. 2. THE TWIDDLE PLAY SDK (a small script that shows ads and pauses the game) - Put these two lines just before the game's own - Start the game's own script with this safety net, copied exactly. It lets the game run in your preview, where the SDK file is missing: window.TwiddlePlay = window.TwiddlePlay || { env: 'preview', ready: function () { return Promise.resolve(); }, showInterstitial: function () { return Promise.resolve({ shown: false }); }, showRewarded: function () { return Promise.resolve({ rewarded: false }); }, hasRewarded: function () { return Promise.resolve(false); }, gameplayStart: function () {}, gameplayStop: function () {}, on: function () { return function () {}; } }; - Use ONLY these SDK functions. Don't invent others: TwiddlePlay.ready(), TwiddlePlay.gameplayStart(), TwiddlePlay.gameplayStop(), TwiddlePlay.showInterstitial(), TwiddlePlay.hasRewarded(), TwiddlePlay.showRewarded(), TwiddlePlay.on(eventName, function) - When the page loads, call TwiddlePlay.ready(). Show the title screen only after it finishes. - Call TwiddlePlay.gameplayStart() when play starts or continues, and TwiddlePlay.gameplayStop() when play stops (game over, level complete, back to the menu). - Full-screen ad: ONLY when the player taps "Play again" or "Next level" on the end screen. First await TwiddlePlay.showInterstitial(), then start the next round. Never during play, never when the game opens, never on a timer. - Rewarded ad: ONE optional button on the end screen, like "Continue (watch ad)" or "Double score (watch ad)". Show it only if await TwiddlePlay.hasRewarded() is true. Give the reward only if (await TwiddlePlay.showRewarded()).rewarded is true. At most once per game. - TwiddlePlay.on('pause', ...): freeze everything (game loop, timers, spawning). TwiddlePlay.on('resume', ...): carry on, and reset the frame clock so nothing jumps. - TwiddlePlay.on('mute', ...): silence all sound. TwiddlePlay.on('unmute', ...): sound back on, unless the player switched sound off with the game's own sound button. 3. PHONE SCREEN AND TOUCH - A portrait game that fills the whole screen on any size, from small 360x640 phones to tall phones and tablets. Re-fit on every resize. - Use and keep the score and buttons inside the safe area with env(safe-area-inset-top), env(safe-area-inset-bottom) and so on. - Touch controls with pointer events (pointerdown), so a mouse works too. Buttons and tap targets at least 48x48 px, with space between them. Nothing that needs hover. - No page scrolling, zooming or text selection: touch-action: none on the canvas, overflow: hidden, user-select: none. - Big, readable text. A sound on/off button on the title screen. 4. SMOOTH ON CHEAP PHONES - One that fills the screen. Size it with devicePixelRatio capped at 2: Math.min(window.devicePixelRatio || 1, 2). - A requestAnimationFrame game loop. Move things by elapsed time (dt in seconds, at most 0.05), never by a fixed amount per frame. - No shadowBlur and no ctx.filter. For glows, draw a gradient once into a small canvas and reuse it. - Reuse objects instead of making new ones every frame. At most about 150 particles. 5. SAVING - Save the best score (and the level and the sound setting, if there are any) in localStorage under the key "bubble-pop.v1". - Wrap every localStorage call in try/catch, so the game still works when storage is blocked. 6. SCREENS - A title screen (name, Play button, best score, sound button), the game, and an end screen (score, best score, Play again, the rewarded button). ```