Imported from KrushnaChaudhary/h5-game-builder-skill (
skill/SKILL.md). Install upstream withnpx skills add KrushnaChaudhary/h5-game-builder-skill --skill skill. Copyright stays with the author.
H5 Game Builder
Turn a prompt — even one line — into a finished, polished, single-file HTML5 game that runs by
double-clicking the file. No build step. The output is one .html holding all code, styling,
design, levels, and asset references. Small assets are base64-embedded for offline play; large assets
are streamed from the user's public h5-assets repo through SHA-pinned jsDelivr URLs. It also serves
as a game-design & art advisor when the user asks questions instead of asking to build (see
Advisor mode).
The goal is not a tech demo. It is a game that feels good in the hand: it onboards a player in seconds, ramps fairly, rewards them, looks designed, and is dripping with juice. A player on a mid-range phone should open it, instantly get it, and want to keep tapping.
Mindset: you are the studio, not a coder
A polished game is design + level design + engineering + art/audio + QA working together. Hold all those hats. Decide the fun first (the core loop), deliver it, make every interaction satisfying, then prove it works on a phone. A technically-correct but boring, confusing, or janky game is a failure even if it runs.
Art comes in tiers — always reach for the highest available
The single biggest quality lever after the core loop is art. Use the best tier available:
- User-supplied
Assets//3D-assets/folders → top tier. If the game directory has anAssets/folder, ingest sprites/audio/fonts; if a 3D game has3D-assets/, ingest models. Embed small assets; host large assets viah5-assets+ jsDelivr. This is the only way to reach illustrated, top-chart art. Always check for them first. Seeassets-pipeline.mdandasset-hosting.md. - Sourced CC0 / curated packs → studio look without user art, when it clearly elevates the game.
See
assets-sourcing.md. - The procedural art-kit → the always-on floor (no files, offline, infinite-DPI). A rich, glossy,
modern-casual look. Use it for everything no asset covers, and to keep tiers cohesive. See
art-kit.md. Never ship flat primitives or emoji as game art.
Step 0 — Read only the reference sections you need (lazy by default)
Do not bulk-read these files. Read the one section for the surface you're building, when you
reach it — jump by heading (the big catalogs art-kit.md and common-bugs.md open with an explicit
section index; for the rest, grep the ## headings). Only game-design.md is read up front. The
chassis comes from the golden you cp (Step 3), so you rarely read a template into context at all.
This lazy discipline is the skill's main token lever; lint + QA gate the OUTPUT, so under-reading a
doc still gets caught, never shipped. Files live in references/:
| File | Read when |
|---|---|
references/game-design.md |
Up front, before code — the one exception. Core loop, difficulty, FTUE, progression, difficulty-curve recipe. |
references/art-kit.md |
When choosing the look — read the top index, then ONLY your chosen preset's section + the inline palette recipe. Not all presets. |
references/ui-art.md |
When building menus / HUD / overlays — the relevant section only. |
references/standard-home-screen.md |
When building HOME — the source of truth for STANDARD_HOME_V1 (shell, locks, direct-play entry, lock sizing, result-panel Home). SKILL.md points here; don't duplicate it from memory. |
references/mobile-optimization.md |
When wiring canvas fit / touch / perf — the relevant section. |
references/polish-juice.md |
When adding juice — the specific recipe (spring, reveal, transition, FX). |
references/assets-pipeline.md |
When an Assets/ folder exists. Ingest + embed the user's art. |
references/asset-hosting.md |
When an asset is too large to embed. Public h5-assets + jsDelivr flow. |
references/assets-sourcing.md |
When real CC0 art/audio would elevate the game. Sourcing + license. |
references/golden-2d.html |
Building a 2D game (the default). cp it, then Edit — don't Read it whole (Step 3). |
references/golden-match3.html |
Swap/cascade grid game (match-3, collapse, blast, merge). Genre-correct grid engine. cp + Edit — don't kludge a grid out of golden-2d. |
references/golden-3d.html |
Building a 3D game. cp + Edit unless the mechanic demands a custom scaffold. |
references/2d-phaser.md / references/3d-threejs.md |
Engine specifics (3D builds from golden-3d.html + 3d-threejs.md + the art-kit 3D preset). |
references/common-bugs.md |
Before claiming done — read only the section matching your symptom (camera, mobile fit, state, audio, memory, z-order). |
references/qa-playtest.md |
Before claiming done. Short QA-gate protocol: dispatch h5-qa-tester. Mandatory. |
references/single-prompt-fastlane.md |
When the prompt is one short line or the user says "you pick". Zero-question path. |
references/reference-match.md |
When the user supplies a reference image to match. Measured refmatch.py extract → compare pipeline (exit 0 required). |
references/boilerplate-mobile.html |
ONLY when building from scratch (rare). The common path cps a golden, which already contains this exact chassis — never read both. |
scripts/lint.sh |
Runnable lint (auto-runs on write). You don't read it — you run it; non-zero exit = not done. |
customizations/README.md |
Auto-installed agents, instructions, hooks. Dispatch the agents instead of reinventing each task. |
Default engines: 2D → Phaser 3, 3D → Three.js, both via CDN so the file stays tiny (only your code + embedded assets count toward the ~2MB budget).
Step 1 — Intake: ask only the open questions, with options
Step 1.0 — Fastlane gate (decide first)
Before asking anything, check whether the single-prompt fastlane applies:
- One short sentence prompt? ("make a runner", "ball-roll game")
- User said "you pick" / "surprise me" / "just build it" / "I don't care"?
- User explicitly wants speed ("quick", "draft", "one-shot")?
If yes → use single-prompt-fastlane.md: zero questions, aggressive defaults, post brief and
build in the same response. Skip the rest of Step 1.
If no → proceed with the intake below.
A one-line prompt can be enough — but ask before assuming anything that would change the whole build. First, three checks you always do silently:
- Check for
Assets/and3D-assets/folders in the game directory. If present, you'll use the user's art (assets-pipeline.md,asset-hosting.md) — and that may inform genre/aesthetic. - Did the user attach or reference an image? A screenshot/mockup of what the game should look
like is the richest intent signal there is — and matching it is a MEASURED process, not a
one-shot glance. Switch to Reference Match mode (below; full pipeline in
references/reference-match.md):scripts/refmatch.py extractsamples the exact palette, you build a custom STYLE from those hexes (never a preset), andrefmatch.py comparemust exit 0 against a screenshot of the built game before you may claim it matches. - Did the user say "feels like "? Map the named reference to concrete
TUNING+ genre values (seegame-design.md"Feels-like map") — e.g. "like Subway Surfers" → 3-lane chase, fastspeed, snappylaneSnapMs, forgivingcoyoteMs. Don't guess vibes; translate to numbers. - Resolve what the prompt already pins from the dimensions below — never re-ask those.
What to ask vs. what to assume
Goal: one round of targeted questions, then commit. Only ask about dimensions the prompt left genuinely open. Skip questions the prompt already answers. Never ask about defaults below (Step 1.5).
For every question you ask, offer 3–5 concrete labelled options (plus "surprise me / you pick").
Don't ask open-ended "what camera do you want?" — the user shouldn't have to invent vocabulary.
Use AskUserQuestion when available so the user can click; otherwise present a numbered list.
The intake dimensions (ask only the unresolved ones)
| Dimension | Default if user says "you pick" | Options to offer when asking |
|---|---|---|
| Genre / core fantasy | One-button arcade dodge-and-collect | Runner • Dodge / collect • Match-3 / merge • Stack / tower • Flappy / tap-to-fly • Shooter • Puzzle / logic • Idle / clicker • Physics sandbox |
| Dimension | 2D unless prompt implies depth | 2D side-view • 2D top-down • 3D |
| Camera / perspective (ask whenever 3D, or when 2D top/side ambiguous) | 2D side-scroll, OR 3D chase for runner / 3D orthographic-isometric for board / 3D orbit for inspect | 2D side-view • 2D top-down • 2D isometric (faux-3D) • 3D third-person chase (runners) • 3D first-person • 3D orthographic isometric (board / puzzle) — see 3d-threejs.md Camera presets table • 3D orbit (inspect / viewer) |
| Control scheme | One-tap (mobile-friendliest) | One-tap • Hold-to-charge • Swipe (4-way / left-right) • Drag • Virtual joystick • Tilt / gyro |
| Aesthetic / style preset — ALWAYS ask this one in any interactive build | Fastlane only: slug-hash rotation over the genre pool (art-kit.md) |
3–4 named directions from the genre's candidate pool, each with palette hex chips + font pairing + one-line vibe (e.g. "Candy Gel — hot pink/mint, chunky rounded, jelly-glossy") • "surprise me" |
| Mood / vibe | Upbeat | Chill / zen • Upbeat / playful • Tense / urgent • Epic / cinematic • Spooky |
| Scope / structure | 3 short levels + endless retry feel | Single endless arcade run • 3 short levels (5–10 min) • 5–10 progressive levels (15–30 min) • Open-ended sandbox |
| Difficulty target | Casual (forgiving early, ramps fair) | Very casual (anyone) • Casual / family • Medium (challenge but fair) • Hard / hardcore |
| Audience | All ages | Kids • All ages • Teen+ • Adult |
| Art source | Whatever tier is highest available | Use my Assets/ folder • Source CC0 for me • Procedural art-kit only |
Routing shortcuts (don't ask if the prompt already says)
| If the prompt… | Decide silently |
|---|---|
| names match-3 / swap / cascade / collapse / blast | Reskin golden-match3.html (grid scaffold). |
| names merge / 2048 | Reskin golden-match3.html → merge deltas (Step 3 table). |
| names a genre (runner, flappy, stack, shooter, dodge) | Use that genre's start point in game-design.md → golden-2d.html. |
| implies depth — "roll", "fly through", "first-person", "rotate a model", "3D" | 3D (Three.js, build from 3d-threejs.md + art-kit 3D preset). |
| anything else (the common case) | 2D → reskin golden-2d.html. |
| is vague ("make a fun game") and the user said "you pick" / "surprise me" | One-button arcade dodge-and-collect — fastest to love. |
Cap the round at 3–4 questions — and the art-direction choice is always one of them (the user picks what their game looks like; you never silently choose for them outside the fastlane). Pick the other dimensions that matter most for this prompt; let the defaults handle the rest. Example: a prompt "make a 3D ball-rolling level" already pins genre + dimension + control — ask only camera (chase vs orbit) and the art direction. A prompt "make a fun game" needs more: genre, art direction, scope. If the user says "you pick" / "just build it", stop asking and commit — slug-hash rotation picks the style (art-kit.md), don't loop.
Step 1.5 — Defaults that ship in every game (NEVER ask the user about these)
These are not options. Implement them in every build without confirmation. Asking the user "do you want a settings button?" is a bug — the answer is always yes.
Standard home contract (STANDARD_HOME_V1) — full spec in references/standard-home-screen.md
(read it when building HOME; it is the single source of truth — don't rebuild it from memory).
The non-negotiable invariants (lint enforces the markers):
- Real themed game name + centered
Level NCTA (persisted N) inHOME_TITLE_CTA_STACK, CTA always below the title. Never a generic Play button or score chip. - Settings gear only top-right (canvas/SVG). No profile, currency, or extra header control.
- 2×2 lock-only feature grid (
HOME_FEATURES: Daily Reward • Daily Puzzle • Spin Wheel • Win Streak) + bottom tray lock-only Shop / active Home / lock-only Leaderboard (HOME_NAV). Locked tiles/tabs render ONLY a centered, correctly-scaled lock medallion — no names, inner art, cart/trophy, or unlock/"Coming soon" copy. - Locked-tap: light haptic (
buzz(~10ms)) + one subtle 1–2px, 120–180ms nudge (no repeated shake; reduced-motion = haptic only), nothing else. DOM builds usearia-disabled="true", neverdisabled(it swallows the pointer event). - Direct-play entry:
const ENTRY_POLICY = 'direct-play';— every load goes LOADING → FTUE/PLAY at the persisted level, never HOME. HOME stays a real separate scene reached from pause/results. - STYLE may reskin colors/materials/fonts, never this composition, item set, lock treatment, or interaction policy.
In-game chassis (always present):
- Settings / pause button (gear icon) visible in the top corner during PLAY at all times. One tap opens a pause panel with Resume • Restart • Home • SFX toggle • Music toggle • Fullscreen.
- Compact pause/settings layout: Resume is the full-width primary action; Restart + Home share a row; SFX/Music/Haptics/Fullscreen use a two-column grid. It must fit at 360x800 without scroll.
- Separate SFX and Music toggles (not a single "mute"). Persist both. SFX off should still allow UI clicks; Music off stops the loop.
- Fullscreen toggle inside the Settings/pause panels using
document.documentElement.requestFullscreen()with a graceful no-op fallback on iOS Safari (hide it ifrequestFullscreenis missing). - Haptics on impact / pickup / win / button press via
navigator.vibrate(ms)— short pulses (10–30ms typical, 80ms for big wins). Gate behind ahapticsOnflag (default on) and respect mute. No-op on desktop; no error if unsupported. prefers-reduced-motionhonored: dampen screen shake, hit-stop, flashes, and big tweens to ~25% intensity when the media query matches.const REDUCED = matchMedia('(prefers-reduced-motion: reduce)').matches; const shake = (ms, amp) => this.cameras.main.shake(ms, REDUCED ? amp * 0.25 : amp);- PostFX bloom on rewards/collectibles + win celebration. Phaser 3.60+:
coin.postFX.addBloom(0xffffff, 1, 1, 1, 1.2). Subtle, not crushing. localStorage-persisted, namespaced by game slug (avoid cross-game collisions):const SAVE_KEY = 'h5_<gameslug>_v1';— stores best score, level/progress, seen-tutorial flag, SFX/Music toggles, haptics toggle. Seegame-design.md.- Game-over screen with score + best + one-tap Retry and Home.
- Level-complete screen between every level (stars/fanfare/particles + Next + Home).
- Final Win screen with Play Again + Home.
- FTUE on first run only, gated by
localStorage, skippable. - Audio-unlock on first tap (mobile requires this).
- Loading screen: progress bar + game name + one short tip line (e.g. "Tap and hold to charge") — never a frozen black screen, never a bare bar.
- Streamed-asset failure handling: any
fetchfor a hosted asset has an 8s timeout and one retry. On final failure, fall back to procedural for that role +console.warnwhat fell back. - Portrait-first mobile fit (
mobile-optimization.md) — no scroll, no clipping, HUD clears notch.
Banned in every game (never optional):
- Emoji as sprites, characters, obstacles, collectibles, or icons. (HUD text emoji like ⭐ in a score readout are tolerable but discouraged — prefer canvas-drawn glyphs.) Icons for settings/sound/fullscreen/back are drawn via canvas or SVG.
- Generic / placeholder game names ("Untitled", "My Game", "New Game", "Test").
- Flat single-color backgrounds. Always a layered scene via
ART.background.
If any of these would be omitted by a literal reading of the user's prompt ("just the gameplay, no menu"), include them anyway — they are the chassis a polished H5 game rides on. The state machine in Step 3 enforces this.
Step 1.6 — The Brief (use this template verbatim)
After the intake round (or immediately, if nothing needed asking), post the Game Design Brief in chat using this template — fixed shape so the user can scan + redirect fast. Keep each line one sentence. No prose paragraphs.
NAME: the real, themed game title (never "Untitled" / "My Game")
PITCH: one sentence a 10-year-old understands
GENRE: e.g. one-tap dodge, match-3, 3D ball-roll
CAMERA: e.g. 2D side, 3D third-person chase, 3D orthographic isometric
CORE LOOP: the 3–10s cycle the player repeats
CONTROLS: tap / hold / swipe / drag / tilt — pick one primary
GOAL: what winning / scoring feels like
FAIL: what losing feels like + retry speed
PROGRESSION: how it ramps (data-driven via LEVELS)
FTUE: how a new player learns it in <10s, no text walls
JUICE: 3 feedback moments that will feel best (e.g. coin pickup, near-miss, level clear)
AESTHETIC: the USER-CHOSEN STYLE preset (+ your ≥2-field customization) + fonts + palette
MUSIC: theme (cozy/chill/bouncy/arcade/epic/spooky/tense) + the CORE-LOOP events that get SFX
ART SOURCE: Assets/ folder | sourced CC0 | procedural art-kit
DEFAULTS: STANDARD_HOME_V1 + direct-play entry + settings + result Home buttons — all IN
Step 1.7 — Greybox preview gate (cheap direction check before the full build)
A full build → "no, not what I meant" → rebuild loop is the most expensive mistake. When the direction has any real ambiguity (a detailed/opinionated prompt, a reference image, an unusual mechanic, or a multi-level scope), render a one-screen greybox first and get a thumbs-up before committing to the full mechanic:
- Build ONLY the standard HOME + one PLAY frame from the golden template: locked shell + real name
Level N+ STYLE palette/fonts + the hero, one collectible, one hazard, HUD, and background. No mechanic logic yet.
- Screenshot it (Preview MCP) and show the user: "This is the look + layout — good to build on?"
- They confirm or redirect the palette/layout/character in seconds, on a 30-second artifact instead of a 10-minute one.
Skip the greybox for the fastlane (one-liner / "you pick" — speed is the whole point there) and for tiny tweaks. Use it whenever a wrong guess would waste a full build. It is a checkpoint, not a deliverable — roll straight into the full build once approved.
Step 2 — Pick a build path (two honest options, no hidden fork)
A single HTML file is tightly coupled — never parallelize writing the file itself (merge chaos). There are exactly two build paths; pick one deliberately. Only the main agent can dispatch agents (subagents have no Task/Agent tool), so the QA→fix loop is always yours either way.
| Crafted (default) | Fastlane (one-shot) | |
|---|---|---|
| Builder | You (main agent) author the file directly | h5-orchestrator builds + lints solo |
| Reference reads | Lazy — only the sections you need (Step 0) | Orchestrator reads inline in its own context |
| Art / levels | Inline recipes (art-kit.md quick-pick, game-design.md curve); dispatch h5-art-director / h5-level-designer only for a deep/custom pass |
Orchestrator does art/levels inline (specialists bypassed — it can't dispatch) |
| Scaffold | cp golden → Edit (Step 3) |
Same, inside the orchestrator |
| QA | You run h5-qa-tester after |
You run it after — orchestrator returns QA: NOT RUN |
| Use when | Most builds — cheap now that reads are lazy; keeps specialist quality reachable | A bare one-liner / "you pick" where a clean main context matters most |
The crafted path is the default — with lazy reads + cp+Edit it costs little main context, and
the inline recipes give you ~80% of specialist quality with no round-trip. Reserve fastlane for
throwaway one-liners. Whichever you pick:
- Never let
h5-orchestratormasquerade as done. It returns a loudQA: NOT RUNbecause it cannot run the browser gate — treat that as unfinished (Step 4). - Specialist agents are optional depth, not a required detour. Dispatch
h5-art-director,h5-level-designer, orh5-asset-prep(in parallel, single message) only when the inline recipes aren't enough — a reference-match STYLE, a tricky difficulty curve, or anAssets/ingest.
The QA gate is always yours: dispatch h5-qa-tester; on fix_required dispatch h5-bug-fixer;
re-QA. Cap at 3 QA→fix cycles. A build is not done until you have a ship verdict.
Step 3 — Build the game (pick the genre scaffold, then reskin)
Pick the scaffold that matches the mechanic — don't force every game onto the runner template.
| Genre | Start from | Notes |
|---|---|---|
| Runner / dodge / collect / flappy / stack / shooter (2D) | references/golden-2d.html |
The default. Reskin look + mechanic, re-tune LEVELS. |
| Match-3 / swap / cascade / collapse / blast | references/golden-match3.html |
Grid engine is genre-correct (swap, match-detect, gravity, cascades, moves+target). |
| Merge (2048-style / merge-tier) | references/golden-match3.html |
Closest sibling — keep its grid + board-fit + chassis; replace match-detect with merge-on-equal + tier-up, swap the swap-input for swipe/drag, change LEVELS to { targetTier, spawnTier }. |
| 3D (ball-roll / chase / first-person) | references/golden-3d.html |
Use 3d-threejs.md for camera, lighting, GLB. |
| 3D board / puzzle (orthographic isometric) | references/golden-3d.html |
Switch the camera to the orthographic-isometric preset in 3d-threejs.md; no OrbitControls. Drive tiles from a grid like golden-match3.html. |
You are editing, not rebuilding — keep the chassis + AUDIO ENGINE intact, replace the MECHANIC and the LOOK, re-tune LEVELS.
Scaffold the file with cp, then Edit — never Read-then-Write the whole golden
The golden templates are 60–80KB. Reading one into context and re-emitting it with Write costs ~40K tokens per build and risks corrupting the verbatim chassis. Don't. Instead:
cpthe genre-matched golden to the game file (byte-for-byte, ~0 tokens):cp <skill-path>/references/golden-2d.html <game>.html. The chassis + QA harness arrive intact and unread.grep -nfor the edit anchors (GAME_SLUG,STYLE,LEVELS,TUNING,<title>, theMECHANICcomment) to get line numbers;Readonly those slices when you need context.Editeach region in place — slug, title, STYLE block, LEVELS array, MECHANIC code, TUNING. Everything you don't touch stays exactly as the golden shipped it. This keeps theH5-CHASSIS-V2block pristine for free (Hard Rule #1) and is the single biggest per-build token saving in the skill.
The chassis block — paste, don't rewrite
Both golden templates already contain the H5 mobile chassis between the <!-- H5-CHASSIS-BEGIN -->
and <!-- H5-CHASSIS-END --> markers. Do not delete or rewrite that block. If you build
from scratch (rare), paste references/boilerplate-mobile.html verbatim and only edit the
clearly-marked GAME_SLUG value. Rewriting the chassis from memory is the single most common
cause of broken-on-mobile games (the lint script in Step 3.5 will catch this and fail).
Required identifiers from the chassis (used by the lint script):
GAME_SLUG • SAVE_KEY (must equal `h5_${GAME_SLUG}_v1`) • DPR_CAP • AUDIO_CTX •
unlockAudio • REDUCED • buzz • toggleFullscreen • STATE • persist • MUSIC +
THEMES (v2 music engine) • QA_HOOKS • the H5QA_REPORT harness • the H5-CHASSIS-V2 marker.
Wire the QA_HOOKS (isReady / getState / getScore / start / pause / resume /
forceGameOver / retry / home / forceLevelComplete) to the real game — the goldens show
the per-engine
Object.assign(window.QA_HOOKS, {...}) pattern at the bottom of the file. Lint fails on empty
stubs; the ?h5qa=1 harness drives the whole QA gate through them.
Set the music theme to match the vibe: MUSIC.setTheme(STYLE.theme) — each art preset carries
a matching theme (cozy / chill / bouncy / arcade / epic / spooky / tense), and you can switch to
'tense' for danger phases. Wire an SFX to every CORE-LOOP event (collect, impact, move,
combo, powerup, UI press, level/win/fail) from the chassis gameplay SFX kit; trim unused entries
(lint fails orphans and warns on thin coverage).
State machine — every game has all of these (none optional)
BOOT → LOADING(progress) → FTUE(first run only) / PLAY ⇄ PAUSE
↘ GAME_OVER → retry / HOME
↘ LEVEL_COMPLETE → next / HOME / WIN
HOME → Level N → PLAY
| State | Must show |
|---|---|
| LOADING | Progress bar — never a frozen black screen. |
| MENU / HOME | STANDARD_HOME_V1: settings-only header; four lock-only feature tiles (no labels); game title; Level N; lock-only Shop/Leaderboard around active Home. |
| FTUE | Visual cue (pointer/arrow) + one short line, gated on first correct input, skippable. |
| PLAY | HUD with live score/lives/timer plus a settings/menu button always visible. |
| PAUSE / SETTINGS | Resume, Restart, Home/Menu, Mute. Must be reachable from PLAY in one tap. |
| GAME_OVER | Score, best, one-tap Retry + Home — back in play within 1s. |
| LEVEL_COMPLETE | Celebration, level number, Next + Home — between every level. |
| WIN | Final score, congrats, Play Again + Home — for level-based games with a last level. |
Run FTUE only on first play (gate behind a localStorage flag). Persist high score, progress,
settings, and the seen-tutorial flag in localStorage.
MENU is its own scene, but it is not the entry scene. The standard HOME must live in a
separate scene/state that transitions via a camera fade into distinct gameplay. Never overlay it
on a live game. Phaser retains separate Menu and Game classes, but Boot starts Game because
ENTRY_POLICY === 'direct-play'; Home buttons later fade to Menu, and the Level N CTA fades
back to Game. In Three.js, start the level directly after loading and keep STATE.MENU as a real
distinct HOME presentation for later navigation. The goldens show both patterns.
Non-negotiables for "polished + mobile"
- Mobile fit contract. Design for portrait first (390×844 and 360×800). Fill the screen with
no stretching, no scroll, no clipped HUD, and no board/tray/world content outside the visible
safe area. Compute board/world scale from available viewport space after reserving HUD and bottom
controls; never hardcode a board size that only fits desktop. Cap pixel ratio at 2. See
mobile-optimization.md. - Touch-first input that also works with mouse. Unify pointer events; prevent scroll/zoom/ pull-to-refresh; unlock audio on first tap.
- Always ship the default chassis (Step 1.5).
STANDARD_HOME_V1, direct-play entry, settings visible in PLAY, pause with Resume / Restart / Home / toggles, Retry + Home on game-over, Next + Home on level complete, Play Again + Home on Win, FTUE, audio unlock, and persistence. Never ask whether to include these. The player must never be trapped or stranded without Home. - Standard camera and scale for 3D. Pick the correct camera preset from
3d-threejs.mdbefore placing objects. Board/puzzle games use fixed orthographic isometric; runners use chase camera; viewer/inspect games may use orbit. Normalize imported GLB models to role target sizes so player, collectibles, tiles, props, and UI-scaled objects read consistently. - Juice + fluid motion everywhere. Eased/spring motion, screen shake, particles, count-up scores,
choreographed reveals, screen transitions (never hard cuts), sound for every meaningful action.
See
polish-juice.md. - Rich, cohesive art (the art-kit floor or better). One
STYLEpreset drives palette + type + shape language. In-world sprites use the layered art-kit recipes (gradient + AO + gloss + rim + contact shadow); characters get faces/personality; collectibles glow; backgrounds are scenes (gradient + glow/hills/rays + vignette + grain), never flat fills; menus/overlays useART.panelframes +ART.button. Never use emoji as sprites, characters, obstacles, or collectibles (HUD text is fine). If anAssets/folder exists, use the real art and tint procedural to match. - A real FTUE — learn by doing, one mechanic at a time, fun within ~10s.
- 60fps on mid-range mobile. Pool objects, cap particles, no per-frame allocation. See
mobile-optimization.md. (Generate art-kit textures once at load, then reuse — never per frame.) - Levels as data. Tune the whole curve via the
LEVELSarray, not buried conditionals. - Feel as data — the
TUNINGblock. Every game-feel constant (input buffer, coyote time, snap/lerp speed, hit-stop, shake amplitude, combo window, count-up duration) lives in one namedTUNINGobject, never hardcoded inside mechanic code. Feel is the thing players judge first — making it a single dial is how you tune "floaty vs tight" in seconds. Lint fails ifTUNINGis declared but unused. Default values are in the golden templates; tune them per game. - Nothing ships missing (completeness). Before done, walk the
BUILD CHECKLISTblock at the top of the golden template: every state reachable + has an exit; every button has a working handler (no dead controls); everysfx.*is actually played;LEVELSis consumed; toggles are read AND written; every brief field has a code home. Lint statically catches orphan SFX, deadLEVELS/TUNING, and one-sided toggles; QA verifies reachability + live wiring by clicking.
Step 3.5 — Pre-flight lint (HARD GATE — game is not done until this passes)
Run the bundled lint script against the built .html. You do not get to skip this. The
lint catches the recurring chassis breakages (mobile contract, audio unlock, reduced-motion,
fullscreen, slug-namespaced save, banned patterns, state constants).
bash <path-to-skill>/scripts/lint.sh path/to/game.html
Exit code is the gate:
- 0 → chassis intact, banned patterns absent. Proceed to Step 4 (QA playtest).
- non-zero → at least one rule failed. Read every
FAILline, fix it, re-run. Do not advance to Step 4 until lint passes.
If you cannot run bash (no shell available in the session), do the equivalent grep checks by
hand against scripts/lint.sh source — every check_required is a positive grep, every
check_banned is a negative grep. But running the script is the path of least mistakes.
Lint is a floor, not a ceiling. Passing it ≠ done; failing it = definitely not done.
Step 4 — Verify before claiming done (mandatory QA gate)
A game you have not watched run is not finished. The verification has two parts and both are mandatory:
-
Static lint (Step 3.5) — already done.
-
Harness + visual QA via subagent — dispatch
h5-qa-tester(protocol inreferences/qa-playtest.md). It loads the game once with?h5qa=1— the built-in chassis harness self-walks every state in ~15s and emits the machine-readableH5QA_REPORTJSON (states, errors, fps, mobile fit, audio wiring, save, clean reset) — then takes a short 3–4-screenshot visual pass for what a machine can't judge (art quality, juice, readability). You then patch and re-QA until verdict isship.Cap at 3 QA passes. If a bug survives 3 patches, stop and surface it in the handoff.
A blocked / QA: NOT RUN verdict is a hard failure, never "done." If h5-qa-tester returns
blocked (no browser MCP callable) or the orchestrator handed back QA: NOT RUN, the build is
unverified — you may not present it as finished. Either obtain browser tooling and run the gate,
or hand off explicitly labelled "UNVERIFIED — QA could not run," listing every item you could not
check. Silence here is the failure mode this rule exists to stop.
If the session has no browser tooling available, say so plainly in the handoff and list what is unverified — never claim success you didn't observe.
The full done-gate checklist lives in references/qa-playtest.md and is encoded as the
h5-qa-tester agent's visual JSON (harness = machine checks; visual pass = judged items). Read
those, don't duplicate the list from memory. Spot-check these top-signal items in the returned
report before claiming done:
- Zero console errors and harness
pass: true. - Direct-entry golden path: LOADING → FTUE/PLAY (never initial MENU) → PAUSE →
LEVEL_COMPLETE → HOME →
Level N→ PLAY → GAME_OVER → Retry (+ WIN for level games), with screenshots of each. - Fits ~390×844 and ~360×800 portrait — nothing cut, stretched, or under the notch.
- Standard HOME exact per
standard-home-screen.md(settings-only header; lock-only features + tabs; name + persistedLevel N; no profile/currency/unlock copy; locked-tap nudges, never navigates). - Every result panel has Home; PLAY has a working gear→pause; SFX/Music toggle independently and persist.
- Art reads as designed (layered sprites, no emoji, grounded, scene background) and juice fires; no leftover template identity; file under ~2MB.
Reference Match mode — "make it look like this image"
When any build or upgrade comes with a reference image (game screenshot, art mockup, UI
wireframe, brand image), matching it is a measured loop, not a vibe. Read
references/reference-match.md for the full pipeline, spec template, and thresholds — don't
run it from memory. The shape in one breath:
- Extract before any code:
refmatch.py extract <image>→ exact palette (hexes are law) + measured per-color positions that seed the LAYOUT MAP +layout.json. - REFERENCE SPEC in the brief (palette chips + layout map + typography + per-element tier plan) so the user corrects your read BEFORE the build.
- Custom STYLE from the sampled hexes — never nearest-preset (dispatch
h5-art-directorin Reference mode for a deep pass). - Compare loop: screenshot REF STATE at the reference's exact aspect →
refmatch.py compare <ref> <shot> --layout layout.json(--strictfor exact-match asks) gates color AND structure → read thediffcomposite → patch ONLY STYLE/ART/layout constants → recompare. Cap 3 rounds. - Reskins are surgical (STYLE /
:root/ ART / layout / ASSETS only, never mechanic/LEVELS/ chassis) and the?h5qa=1harness must still pass. Never claim "matched" without an exit-0 compare; hand off the tier table + final numbers.
Upgrade mode — "Improve this existing game"
When the user asks to improve an existing game ("make it beautiful", "upgrade the art", "fix the camera angle", "rebalance levels", "make objects larger", "change colors", "add juice"), treat it as an H5 upgrade/patch, not a new build. Preserve the working chassis and only replace what the prompt targets:
- Inspect current state first. Read the
.html, identify engine, state machine, art system, camera/layout, LEVELS, TUNING, and assets. - Keep the user's game identity unless they ask for a reskin. Preserve core mechanic and progression shape, but rewrite art/style/levels/camera/tuning when those are the requested change.
- Use the same quality bar as new builds. Read the relevant references:
ui-art.md,art-kit.md,game-design.md,polish-juice.md,mobile-optimization.md, and 3D/camera docs when applicable. - Retrofit the standard chassis if missing. If the file predates the current contract —
no
STANDARD_HOME_V1marker, no lock-only home shell, no locked-tap feedback, no direct-play entry, or missing result-panel Home buttons — port the missing pieces from the matching golden template as part of the upgrade, wired to the game's existing state machine and persisted level. Lint fails without them, so the upgrade is not done until they exist. - Patch surgically, then verify. Run lint, then QA. Existing-game upgrades are not complete until the improved game still loads, fits mobile, and every edited state works.
Repair mode — "I built a game with this skill and it broke"
When the user comes back with a broken build ("the camera is wrong", "it crashes on retry", "the pause button doesn't work"), don't rebuild from scratch. Diagnose and patch.
- Read the file. Quickly map state machine, camera setup, input handlers.
- Reproduce. Dispatch a QA subagent (
qa-playtest.md) to load the file and confirm the reported symptom. Capture console + screenshots. Don't trust the user's diagnosis — verify the symptom yourself. - Cross-reference
common-bugs.md. Match the symptom to an entry. The fix is usually one of the 45 cataloged. - Patch surgically. Make the smallest change that fixes the bug — don't rewrite. Preserve
the user's level data, art tier choices, and aesthetic. Exception: if the file lacks the
STANDARD_HOME_V1shell or its contract, retrofit it in the same pass (lint fails without it). - Re-QA. Dispatch the same subagent against the patched file. Confirm the bug is gone and nothing else regressed.
- If the same bug recurs across patches: that means a deeper architecture issue. Surface it to the user (e.g. "the camera fix needs a different camera preset entirely — your current game uses orbit but it should be orthographic isometric. OK to switch?").
Repair mode is the most-used path for an installed skill — the chassis is good, individual bugs are common. Default to "diagnose + patch," not "rebuild."
Advisor mode — answer game & art questions, don't just build
When the user asks a question rather than "build X" — "how do I make this feel better?", "what
aesthetic fits a calm puzzle?", "how should difficulty ramp?", "why does my jump feel floaty?",
"what art style for a kids' game?" — answer it using the references (game-design.md,
ui-art.md, art-kit.md, polish-juice.md, mobile-optimization.md). Give a concrete, opinionated
answer with specifics (values, easings, palettes, presets), not generalities. Then offer to apply it
(build or patch the game) if useful. Keep the builder workflow above for when they actually want code.
Output and handoff
Deliver the single .html and a 3–4 line summary: the pitch, the controls, how to add/tune levels
(point at the LEVELS array), which art tier was used (assets / sourced / procedural) with the file
size, and — if Assets/ or 3D-assets/ were used — which roles used real art vs. procedural
fallback, which assets were embedded, and which were hosted. Don't write a separate design doc unless
asked.