← COCOFLY
Для авторов игр · шаблон «Поймай крошку»

Создай свою игру для COCOFLY

Пайплайн, готовые промпты и честные советы: новая мини-игра с Claude — с минимумом токенов.

В «Играх» Коко больше двадцати мини-игр. Новая игра — это один файл в web/src/games/, три строки регистрации и (по желанию) один тест. Этот гайд — короткий путь от идеи до пулл-реквеста. Пример, с которого всё копируется, — игра «Шаблон: Поймай крошку» (web/src/games/template.js): она уже есть в каталоге, в неё можно сыграть.

Содержание:

Коротко: 9 шагов

  1. Идея в одном предложении — и бриф из пяти строк.
  2. Дело Коко, к которому привязана игра (контекст): приставка, утюг, балкон…
  3. Скопируй шаблон: template.js → <id>.js.
  4. Дай Claude только нужное: бриф + template.js + шпаргалку API. Не весь репозиторий.
  5. Попроси один файл — готовым промптом ниже.
  6. Запусти: npm run dev → вкладка «Игры» → твоя карточка.
  7. Проверь тестом: node --test tests/<id>.test.mjs.
  8. Зарегистрируй — три строки в трёх файлах.
  9. Собери и отправь PR: npm test && npm run build.

Пайплайн по шагам

1. Идея в одном предложении

Формула: «Коко [занята делом] — игрок [одно действие], чтобы [цель] за [время]».

Правило: одно действие, одна цель, один твист. Если идея не влезает в предложение — это две игры.

Сразу запиши бриф из пяти строк. Его вставляют в каждый запрос вместо пересказа, а живёт он в шапке файла игры — тогда он попадает к Claude вместе с кодом бесплатно.

Игра: iron — «Утюг» / "The iron"
Дело Коко: iron (появляется в «Сейчас можно», когда Коко гладит)
Действие: вести утюг пальцем или ← → ↑ ↓; A — пар
Цель: разгладить 80% складок за 30 с; уровень — больше складок, утюг остывает быстрее
Твист: носки прилипают к утюгу; стиль — тёплая гладильная доска, пар частицами

2. Привяжи игру к делу Коко

У игры два режима, и оба работают сами:

Выбери id дела. Свободные дела ждут своих игр:

Дело (id)Что делает КокоИгры сейчас
gameИграет в приставкуГонки, Танчики, Мухобой, Оборона банана, Сахарница, Дорога, Сумерки
laptop, workНоутбук, офисКосынка, Почта, Презентация, Очередь к кулеру, Сапёр
phoneЛистает телефонМухо-тиндер, Сапёр
cook, microwave, toastГотовитНе сожги!
bath · treadmill · groom, wash, showerВанна · дорожка · умываетсяПузыри · Беговая дорожка · Груминг-ритм
unpack, tidy, vacuumПосылки, уборкаБашня из коробок, Курьер у двери, Крошки на ковре
walk, travelГуляет, в дорогеДорога, Сумерки
snack, eatПерекусывает, естШаблон: Поймай крошку
iron, hang, laundry, dishes, mopГладит, бельё, посуда, лужа— свободно
water_plants, coffee, water, hairdryЦветы, кофе, вода, фен— свободно
vinyl, read, dance, stretch, napВинил, книга, танцы, разминка— свободно
balcony, sunbathe, stare, chaseБалкон, подоконник, окно, мотылёк— свободно
host, mom, order, search, hideГости, мама, маркетплейс, ключи— свободно

Полный список дел — web/src/core/actions.js. Условие появления пишется одной функцией: doing('iron') или, например, (g) => g.s.clutter > 25 && doing('vacuum')(g).

3. Скопируй шаблон

cp web/src/games/template.js web/src/games/iron.js
cp tests/template.test.mjs tests/iron.test.mjs

Шаблон — около 170 строк кода с комментариями: контракт, пауза, касания и клавиши, эффекты, итог. В копии поменяй ID = 'template' на свой id, имена templateGame / TEMPLATE_GAMES / ключ template: — или поручи это Claude в том же запросе.

4. Дай Claude только нужный контекст

Нужно ровно это:

  1. Бриф — 5 строк из шага 1.
  2. **web/src/games/template.js** целиком — он же контракт и образец стиля.
  3. Шпаргалка API — блок ниже, ~30 строк вместо трёх файлов.
  4. Только если механика похожа на существующую игру — одна функция оттуда (например, spawn), не файл.

Не нужно: main.js, весь games.js (1700+ строк), симуляция, 3D-сцена, CSS, другие игры, логи сборки. Базовые проверки не надо объяснять — tests/games.test.mjs и tests/freeplay.test.mjs сами прогоняют каждую зарегистрированную игру.

5. Попроси одну игру — один файл

Возьми промпт ниже, подставь бриф и два вставки — и отправь одним сообщением. Ответ: файл игры, тест и три строки регистрации. Если Claude задал вопросы — ответь коротко в том же чате.

6. Запусти локально

# один раз
npm install
# игра на http://127.0.0.1:4173
npm run dev

Открой вкладку «Игры» → твоя карточка в каталоге → играй (свободный режим). С телефона: npm run dev -- --host, потом на iPhone в той же Wi-Fi открой http://<IP-компьютера>:4173. Посмотреть контекст (монеты, «Сейчас можно») можно, дождавшись дела Коко, или отладочной строкой в консоли браузера — она меняет текущий забег, лучше в окне инкогнито:

Object.assign(__cocofly.g.action, { id: 'iron', route: [], routeIndex: 0, total: __cocofly.g.action.elapsed + 60 })

7. Проверь тестом

# твоя игра — секунды
node --test tests/iron.test.mjs
# общие проверки для неё же: случайный ввод, один итог, свободная игра, без утечек
node --test --test-name-pattern=iron tests/games.test.mjs tests/freeplay.test.mjs
# всё перед PR — около минуты
npm test

Тест — лучшее ТЗ: вместо абзаца «хочу, чтобы при паузе время стояло» напиши (или попроси) тест, и дальше говори Claude «сделай, чтобы прошёл».

8. Зарегистрируй: три строки

// web/src/games/games.js — импорт рядом с остальными и строка в GAMES
import { IRON_GAMES } from './iron.js';
// min — длина партии + 5 с
  iron: { ...IRON_GAMES.iron, when: doing('iron'), min: 35 },

// web/src/ui/gamesview.js — WHEN: группа каталога и «когда появляется»
  iron: ['home', T('когда Коко гладит бельё', 'when Coco is ironing')],

// web/src/core/freeplay.js — FREE_ACTION: какое дело «изображает» свободная партия
  iron: 'iron',

Группы каталога: console, office, phone, road, home. По желанию — влияние на Коко в контексте: одна строка в applyMatchEffects в web/src/main.js (например, if (id === 'iron') s.stress = gclamp(s.stress - (r.won ? 5 : 1));). Монеты и настроение за победу начисляются и без этого.

9. Собери и отправь PR

npm test && npm run build
git checkout -b game-iron
git add web/src/games/iron.js tests/iron.test.mjs web/src/games/games.js web/src/ui/gamesview.js web/src/core/freeplay.js CHANGELOG.md
git commit -m "Новая мини-игра «Утюг» (iron)"
git push -u origin game-iron

В CHANGELOG.md — одна строка под «Unreleased». В описании PR: бриф, скриншот с телефона, результат npm test. Правила проекта — в CONTRIBUTING.md.

Промпт: новая игра за один раз

Скопируй, замени то, что в <угловых скобках>, вставь три блока внизу.

Ты пишешь мини-игру для COCOFLY (Vite, canvas 2D, без библиотек). Ниже: бриф, шаблон template.js (он же контракт) и шпаргалка API.

Сделай файл web/src/games/<id>.js по образцу шаблона:
- тот же контракт: export function <id>Game(container, ctx) → { stop(), timeUp() } и export const <ID>_GAMES = { <id>: { name: T(…), desc: T(…), run, icon } }; бриф — 5 строк в шапке файла;
- stage() из art/shell.js; эффекты только из gfx.js и art/*.js; Коко — fly() из art/fly.js; никаких новых зависимостей и внешних картинок;
- все тексты через ctx.t('рус', 'eng'); итог — ровно один раз через S.end(result): { won, score, timeUp, progress 0…1, key, stats: 2–4 шт. };
- длительность DUR = min(дизайн, ctx.budget − 1); ctx.level немного усложняет игру;
- управление: палец (down/move) и клавиши ← → ↑ ↓, Пробел (A), Enter (B); пауза при visibilitychange и blur, продолжение — касание или клавиша;
- 60 fps на iPhone: фон и статика через sprite(), в кадре без filter()/map() и новых массивов, shadowBlur не больше 1–2 раз за кадр;
- ID = '<id>' в ключах спрайтов; ctx.debug.state() для тестов.
Затем: 1) tests/<id>.test.mjs по образцу tests/template.test.mjs (если я его вставил): бот выигрывает, timeUp даёт один результат, stop() без утечек; 2) три строки регистрации (games.js, WHEN в gamesview.js, FREE_ACTION в freeplay.js).
Только код, без пояснений. Если в брифе что-то неясно — сначала до трёх коротких вопросов.

БРИФ:
<5 строк брифа>

TEMPLATE.JS:
<содержимое web/src/games/template.js>

ШПАРГАЛКА API:
<блок «Шпаргалка API» с этой страницы>

ТЕСТ (по желанию):
<содержимое tests/template.test.mjs>

Для больших переделок сначала попроси план — это дешевле, чем переписывать код дважды:

Большое изменение в web/src/games/<id>.js: <что хочу, 1–2 предложения>. Сначала только план: какие функции меняешь и что добавишь, риски — до 10 строк. Код не пиши, жди моего «ок».

Промпт: правка одним diff

Правка в мини-игре COCOFLY, файл web/src/games/<id>.js (ниже — файл или только нужная функция).
Задача: <одна правка, 1–2 предложения>.
Ответь только изменениями в формате unified diff (как git diff). Не переписывай файл целиком, не трогай то, о чём не просили, без пояснений. Контракт не ломать: тексты через ctx.t, итог через S.end ровно один раз, stop() снимает всё.

<файл или функция>

И для ошибки — только первая строка и немного стека:

Ошибка в web/src/games/<id>.js:
<первая строка ошибки + до 5 строк стека>
Функция, где падает:
<функция>
Найди причину и дай минимальный diff. Пояснение — не длиннее двух предложений.

Чек-лист перед PR

Как тратить меньше токенов

Токены — это и деньги, и лимиты. Что реально помогает:

  1. Выдержки, а не файлы. games.js — 138 КБ, по грубой оценке ~40 тыс. токенов; весь web/src — больше 2 МБ, в запрос не влезет и не нужен. template.js — около 4 тыс. токенов, и его хватает.
  2. Проси diff, а не файл целиком. Ответ модели (выходные токены) обычно в несколько раз дороже входа. Перепечатка 170 строк ради одной поправки — самая дорогая привычка.
  3. Одна правка — одно сообщение. «Сделай быстрее, красивее и добавь босса» рождает большой ответ и откаты. Три коротких запроса дешевле одного размытого.
  4. Не описывай эффекты — называй функции. «shake.kick(6) + flash.trigger('#fff', .4) + ps.burst из точки удара» короче абзаца про «сочный удар» и даёт ровно то, что нужно.
  5. Бриф из 5 строк вместо пересказа. Держи его в шапке файла игры — он приходит к Claude вместе с кодом.
  6. Для больших изменений — «сначала план, стоп». Ошибку в плане из 10 строк исправить дешевле, чем в 200 строках кода.
  7. Новая игра — новый чат. В длинном чате вся история отправляется заново с каждым сообщением, и каждое следующее дороже. Закончил игру — начни чистый чат (в Claude Code — /clear).
  8. Тест вместо объяснений. Тест однозначен, короче описания и проверяет результат без твоего участия.
  9. Из лога — только первая ошибка. Первая строка и до пяти строк стека. Весь лог сборки — тысячи токенов шума.
  10. Скриншот — обрезанный. Картинка стоит примерно (ширина × высота) / 750 токенов после уменьшения, обычно 1–1,6 тыс. Обрежь до холста игры — и это часто дешевле трёх абзацев описания бага.
  11. Модель по задаче. Младшая быстрая модель (например, Haiku) — переименовать, поменять числа и тексты. Средняя (Sonnet) — обычная новая игра. Старшая (Opus) — дизайн механики, сложная физика или ИИ соперника, трудные баги.
  12. Кэш и знания проекта. В claude.ai положи template.js, этот гайд и шпаргалку в знания проекта: их не надо вставлять в каждый чат, а по справке Anthropic содержимое проекта кэшируется и повторно расходует лимит заметно меньше. В API — prompt caching: неизменную часть (шаблон, шпаргалку) ставь в начало и помечай cache_control; чтение из кэша стоит около 10% обычной цены входа, кэш живёт минуты — работай сериями.
  13. В Claude Code — пути, а не вставки. «Прочитай только web/src/games/template.js и раздел #api-ru из docs/CREATE-A-GAME.md, создай web/src/games/iron.js по брифу…» — модель не будет бродить по репозиторию.
  14. «Без пояснений — только код». Одна фраза в конце запроса заметно сокращает ответ.
  15. Комментарии в коде — коротко и по-английски. Русский текст обычно дороже по токенам, а код с комментариями ты отправляешь снова и снова. Тексты игры, конечно, двуязычные.

Сколько контекста давать:

ЗадачаЧто дать ClaudeОбъём
Поменять число, текст, цветничего — поменяй сам; или одну строку0–5 строк
Исправить багпервую ошибку + функцию, где падает1 ошибка + 20–60 строк
Новая механика в своей игребриф + файл игры, ответ — diff1 файл
Новая игра с нулябриф + template.js + шпаргалка (+ тест)~250 строк
Игра «как вон та»+ одна функция из похожей игры+50–100 строк
Влияние на Кокобриф + applyMatchEffects из main.js~30 строк
Регистрацияничего — три строки из шага 80
Красота: частицы, тряска, вспышкиназвания функций из шпаргалки + обрезанный скриншот5–10 строк

Шпаргалка API для Claude

Вставляй этот блок вместо файлов gfx.js, art/*.js и pad.js.

COCOFLY mini-game API (paste instead of the source files)
// gfx.js — import { … } from './gfx.js'
particles(limit) → ps: ps.add({x,y,vx,vy,life,size,grow,color,gravity,drag,shape:'dot'|'spark'|'square'|'ring'|'text',glow,fade}), ps.burst(x,y,n,{speed,spread,angle,life,size,colors,gravity,drag,shape}), ps.update(dt), ps.draw(g), ps.clear()
popups() → pops.add(x,y,text,color,size,life), pops.update(dt), pops.draw(g)      // "+10", "streak ×3"
shaker() → shake.kick(px), shake.update(dt), shake.begin(g) … shake.end(g)       // screen shake around drawing
flasher() → flash.trigger(color,strength), flash.update(dt), flash.draw(g,W,H)   // full-screen flash
timewarp() → tw.freeze(s), tw.slow(factor,s), dt = tw.scale(dt)                  // hit-stop, slow-mo
glowText(g,text,x,y,{font,color,stroke,width,glow,align})                        // outlined text
vignette(g,W,H,a) · scanlines(g,W,H,a) · glow(g,x,y,r,'r,g,b',a) · shadow(g,x,y,rx,ry,a) · vgrad(g,x,y,w,h,[[0,c],[1,c]])
sprite(key,w,h,(g)=>paint) → cached offscreen art, painted once · blit(g,spr,x,y,{rot,sx,sy,alpha}) centred
ease.outCubic|inOutSine|outBack|outElastic(t) · lerp · clamp01 · rand(a,b) · pick(arr) · TAU
// art/shell.js — the plumbing (hi-DPI canvas via gfx.hidpi, dt loop ≤ 0.05 s, rules card, input routing, one finish)
S = stage(container,ctx,W,H,{title,lines[],card:{accent,panel,icon},key(e,down),down(x,y,e),move(x,y,e),up(x,y,e),leave(),blur(),timeUp()→result,update(dt),draw(g)})
S.end(result,delayMs) → ctx.finish once · S.t (s since start) · S.intro · S.pressed · S.stop() · S.timeUp()
// input: x,y are logical px; the handheld pad sends keys: D-pad = Arrow*, A = ' ', B = START = 'Enter', SELECT = 'Shift'
// art/fly.js — Coco
fly(g,x,y,size,{body,eyes,flap,rest,tilt,mouth:'smile'|'grin'|'o'|'frown'|'flat',blush,sweat,shades,alpha,squash})
// art/ui.js
bar(g,x,y,w,h,k,color,track) · ring(g,x,y,r,k,color,width) · fitText(g,text,x,y,maxW,weight,px) · rrp(g,x,y,w,h,r) path · softShadow(g,x,y,w,h,r,a) · confetti(ps,x,y,n) · approach(a,b,rate,dt)
icon(g,name,x,y,s,color): clock flag star heart x check box candy cube crumb mail slides drop sparkle bolt refresh trophy hand eye wing broom cards play battery chat
// native/haptic.js — haptic('tap'|'light'|'medium'|'heavy'|'success'|'warning'|'error')
// kit.js — T(ru,en) for registry strings
// ctx — t(ru,en) · sfx('eat'|'bump'|'click'|'accept'|'refuse'|'achievement'|'cardboard'|'crash'|'takeoff') · level 1…99 · mods Set('fast'|'night'|'mirror') · budget s · mins(n) · started() · finish(r) · debug (tests)
// registry entry — { name:T, desc:T, icon:'🍪', run, when:(g)=>bool, min:s, shell?:'handheld'|'laptop'|'phone'|'plain', theme?:'lime'|'coral'|'sky'|'graphite' }

Контракт мини-игры

run(container, ctx) получает контейнер (экран карманной консоли) и ctx от хоста (main.js), возвращает { stop(), timeUp() }.

Поле ctxЧто это
t(ru, en)строка на языке игрока
sfx(name)звук: eat, bump, click, accept, refuse, achievement…
levelуровень игры 1…99, растёт с каждой победой
modsSet твистов с 3-го уровня: fast, night, mirror
budgetсколько секунд партии разрешает хост: в контексте — остаток дела Коко, в свободной игре — 120
mins(n)подпись времени в единицах хоста: «12 с» или «12 мин»
started()карточка правил закрыта (stage() зовёт сам)
finish(result)итог — ровно один раз (через S.end)
debugобъект только в тестах: сюда игра кладёт state()

Итог: { won, score, timeUp, progress, key, stats }. won — победа; score — очки для рекордов; timeUp — партию оборвал хост; progress — доля цели 0…1; stats — 2–4 строки { id, label, value } для экрана итогов; key — id главной из них.

stop() — хост закрывает игру: сними все слушатели, кадры и таймеры. timeUp() — дело Коко закончилось: верни то, что успели (в шаблоне stage сам вызовет finish).

Что делает stage() за тебя: чёткий холст под плотность экрана, цикл с dt, карточку правил с таймером, касания и мышь в логических координатах, клавиатуру и кнопки консоли, защиту от падения кадра, освобождение памяти холстов на iPhone и один отложенный finish.

For game makers · the "Catch the crumb" template

Make your own COCOFLY game

The pipeline, ready-made prompts and honest tips: a new mini-game with Claude on as few tokens as possible.

Coco's «Игры» tab holds more than twenty mini-games. A new game is one file in web/src/games/, three registration lines and (optionally) one test. This guide is the short road from an idea to a pull request. The example everything is copied from is "Template: Catch the crumb" (web/src/games/template.js) — it is already in the catalogue, you can play it.

Contents:

In short: 9 steps

  1. The idea in one sentence — and a five-line brief.
  2. Coco's activity the game belongs to (context): the console, the iron, the balcony…
  3. Copy the template: template.js → <id>.js.
  4. Give Claude only what it needs: the brief + template.js + the API cheat sheet. Not the whole repo.
  5. Ask for one file with the prompt below.
  6. Run it: npm run dev → the Games tab → your card.
  7. Test it: node --test tests/<id>.test.mjs.
  8. Register it — three lines in three files.
  9. Build and open a PR: npm test && npm run build.

The pipeline, step by step

1. The idea. Formula: "Coco is [doing something] — the player [one action] to [goal] within [time]". One action, one goal, one twist; if it does not fit one sentence, it is two games. Write the five-line brief right away and keep it in the header of the game file, so it travels to Claude with the code:

Game: iron — «Утюг» / "The iron"
Coco's activity: iron (shows up under «Сейчас можно» while Coco irons)
Action: drag the iron with a finger or ← → ↑ ↓; A = steam
Goal: smooth 80% of the creases in 30 s; levels add creases and cool the iron faster
Twist: socks stick to the iron; look: a warm ironing board, steam as particles

2. The activity. Every game has two modes and both work by themselves: free play (the catalogue card, always open: records and levels, Coco untouched) and context (while Coco does that activity, the game shows up under «Сейчас можно» and on Home and pays her coins and mood; the match length is what is left of her activity, ctx.budget). Pick an activity id from the table in the Russian section or web/src/core/actions.js; free ones include iron, hang, laundry, dishes, mop, water_plants, coffee, vinyl, read, dance, balcony, sunbathe, chase, host, mom. The rule is one function: doing('iron'), or e.g. (g) => g.s.clutter > 25 && doing('vacuum')(g).

3. Copy the template.

cp web/src/games/template.js web/src/games/iron.js
cp tests/template.test.mjs tests/iron.test.mjs

Then rename ID = 'template', templateGame, TEMPLATE_GAMES and the template: key — or let Claude do it in the same request.

4. Minimal context. Exactly: the brief; web/src/games/template.js in full (it is the contract and the style guide); the API cheat sheet (~30 lines instead of three files); only if the mechanic resembles an existing game — one function from it, not the file. Not needed: main.js, the whole games.js (1700+ lines), the simulation, the 3D scene, CSS, other games, build logs. The generic checks need no explaining: tests/games.test.mjs and tests/freeplay.test.mjs run every registered game.

5. Ask for one file with the prompt below, in one message. You get the game file, a test and three registration lines.

6. Run it locally.

# once
npm install
# the game at http://127.0.0.1:4173
npm run dev

Open «Игры» → your card in the catalogue → play (free mode). On a phone: npm run dev -- --host, then open http://<computer-IP>:4173 on the iPhone in the same Wi-Fi. To see context mode, wait for Coco's activity or use this debug line in the browser console (it changes the current run — use a private window):

Object.assign(__cocofly.g.action, { id: 'iron', route: [], routeIndex: 0, total: __cocofly.g.action.elapsed + 60 })

7. Test it.

# your game — seconds
node --test tests/iron.test.mjs
# the generic checks for it: random input, one result, free play, no leaks
node --test --test-name-pattern=iron tests/games.test.mjs tests/freeplay.test.mjs
# everything before a PR — about a minute
npm test

A test is the best spec: instead of a paragraph ("time must stand still in a pause") write — or ask for — a test and then say "make it pass".

8. Register it: three lines.

// web/src/games/games.js — the import next to the others and a line in GAMES
import { IRON_GAMES } from './iron.js';
// min — the match length + 5 s
  iron: { ...IRON_GAMES.iron, when: doing('iron'), min: 35 },

// web/src/ui/gamesview.js — WHEN: catalogue group and "when it shows up"
  iron: ['home', T('когда Коко гладит бельё', 'when Coco is ironing')],

// web/src/core/freeplay.js — FREE_ACTION: the activity a free match pretends Coco is doing
  iron: 'iron',

Catalogue groups: console, office, phone, road, home. Optional effect on Coco in context: one line in applyMatchEffects in web/src/main.js; coins and mood for a win come without it.

9. Build and open a PR.

npm test && npm run build
git checkout -b game-iron
git add web/src/games/iron.js tests/iron.test.mjs web/src/games/games.js web/src/ui/gamesview.js web/src/core/freeplay.js CHANGELOG.md
git commit -m "New mini-game: The iron (iron)"
git push -u origin game-iron

One CHANGELOG.md line under "Unreleased"; in the PR: the brief, a phone screenshot, the npm test result. Project rules: CONTRIBUTING.md.

Prompt: a new game in one shot

You are writing a mini-game for COCOFLY (Vite, canvas 2D, no libraries). Below: the brief, the template template.js (it is also the contract) and the API cheat sheet.

Create web/src/games/<id>.js following the template:
- the same contract: export function <id>Game(container, ctx) → { stop(), timeUp() } and export const <ID>_GAMES = { <id>: { name: T(…), desc: T(…), run, icon } }; the 5-line brief in the file header;
- stage() from art/shell.js; effects only from gfx.js and art/*.js; Coco is fly() from art/fly.js; no new dependencies or external images;
- every text through ctx.t('ru', 'en'); the result exactly once through S.end(result): { won, score, timeUp, progress 0…1, key, stats: 2–4 };
- duration DUR = min(design, ctx.budget − 1); ctx.level makes it a bit harder;
- controls: a finger (down/move) and ← → ↑ ↓, Space (A), Enter (B); pause on visibilitychange and blur, resume on a tap or a key;
- 60 fps on an iPhone: background and static art through sprite(), no filter()/map() or new arrays per frame, shadowBlur at most 1–2 times per frame;
- ID = '<id>' in sprite keys; ctx.debug.state() for tests.
Then: 1) tests/<id>.test.mjs modelled on tests/template.test.mjs (if I pasted it): a bot wins, timeUp gives one result, stop() leaks nothing; 2) the three registration lines (games.js, WHEN in gamesview.js, FREE_ACTION in freeplay.js).
Code only, no explanations. If the brief is unclear, first ask up to three short questions.

BRIEF:
<the 5-line brief>

TEMPLATE.JS:
<contents of web/src/games/template.js>

API CHEAT SHEET:
<the "API cheat sheet" block from this page>

TEST (optional):
<contents of tests/template.test.mjs>

For big changes ask for a plan first — cheaper than writing the code twice:

A big change in web/src/games/<id>.js: <what I want, 1–2 sentences>. First only a plan: which functions change, what gets added, risks — up to 10 lines. No code until I say "ok".

Prompt: an edit as one diff

An edit in a COCOFLY mini-game, file web/src/games/<id>.js (below: the file or just the function).
Task: <one change, 1–2 sentences>.
Answer only with the changes as a unified diff (like git diff). Do not reprint the whole file, do not touch anything else, no explanations. Keep the contract: texts through ctx.t, the result through S.end exactly once, stop() removes everything.

<the file or the function>

For a bug, only the first error and a little stack:

An error in web/src/games/<id>.js:
<the first error line + up to 5 stack lines>
The function where it fails:
<the function>
Find the cause and give a minimal diff. Explanation: two sentences at most.

Checklist before a PR

Spending fewer tokens

  1. Excerpts, not files. games.js is 138 KB, roughly ~40k tokens; all of web/src is over 2 MB — it does not fit and is not needed. template.js is about 4k tokens and is enough.
  2. Ask for a diff, not the whole file. Output tokens usually cost several times more than input; reprinting 170 lines for one fix is the most expensive habit.
  3. One change per message. "Faster, prettier and add a boss" produces a long answer and rollbacks; three short requests are cheaper than one vague one.
  4. Name functions instead of describing effects. "shake.kick(6) + flash.trigger('#fff', .4) + ps.burst at the hit point" is shorter than a paragraph about a "juicy hit" and gets exactly that.
  5. A five-line brief instead of re-explaining. Keep it in the game file's header — it reaches Claude with the code.
  6. For big changes: "plan first, then stop". Fixing a 10-line plan is cheaper than fixing 200 lines of code.
  7. New game — new chat. A long chat re-sends the whole history with every message, so each message costs more. Finished a game — start a clean chat (in Claude Code: /clear).
  8. A test instead of explanations. It is unambiguous, shorter than prose and checks the result without you.
  9. From a log, only the first error — the first line and up to five stack lines. A whole build log is thousands of tokens of noise.
  10. Cropped screenshots. An image costs about (width × height) / 750 tokens after downscaling, typically 1–1.6k. Crop to the game canvas — often cheaper than three paragraphs describing a bug.
  11. The model for the job. A small fast model (e.g. Haiku) for renames, numbers and texts; a mid one (Sonnet) for an ordinary new game; the largest (Opus) for mechanic design, tricky physics or AI opponents, hard bugs.
  12. Caching and project knowledge. On claude.ai, put template.js, this guide and the cheat sheet into the project knowledge: no pasting in every chat, and according to Anthropic's help pages project content is cached, so reusing it counts noticeably less against limits. In the API, use prompt caching: put the unchanging part (template, cheat sheet) first and mark it with cache_control; cache reads cost about 10% of the normal input price and the cache lives for minutes — work in bursts.
  13. In Claude Code, give paths, not pastes. "Read only web/src/games/template.js and the #api-ru section of docs/CREATE-A-GAME.md, create web/src/games/iron.js from this brief…" — the model will not wander around the repo.
  14. "No explanations, code only." One sentence at the end of a request shortens the answer a lot.
  15. Code comments: short and in English. Russian text usually costs more tokens, and you send the commented code again and again. The game's own texts stay bilingual, of course.

How much context to give:

TaskWhat to give ClaudeSize
Change a number, a text, a colournothing — do it yourself; or one line0–5 lines
Fix a bugthe first error + the failing function1 error + 20–60 lines
A new mechanic in your gamethe brief + the game file, answer as a diff1 file
A new game from scratchthe brief + template.js + the cheat sheet (+ the test)~250 lines
A game "like that one"+ one function from the similar game+50–100 lines
An effect on Cocothe brief + applyMatchEffects from main.js~30 lines
Registrationnothing — the three lines from step 80
Juice: particles, shake, flashesfunction names from the cheat sheet + a cropped screenshot5–10 lines

The contract table, what stage() does for you and the API cheat sheet are in the Russian section: contract, API cheat sheet (the cheat sheet itself is in English).