storebound/skills/store-screenshots/SKILL.md

10 KiB

name description
store-screenshots Produce store screenshots and the Play feature graphic for a mobile game in every required size and many languages - capture raw frames from the running game (web build via playwright, iOS Simulator via simctl, Android emulator or device via adb, or a folder of frames from any engine), write per-language marketing copy (incl. RTL Arabic and CJK fonts), render captioned scenes with device frames, review them in a local gallery, A/B variants for store listing experiments / Product Page Optimization, JPEG copies for Play, and upload to both stores. Use when the user needs App Store / Google Play screenshots, preview images, a feature graphic, localized screenshot captions, or screenshot sizes.

Store screenshots

CLI: storebound (or bunx storebound in a project that has it, or bun <toolchain>/cli/index.ts). Needs ffmpeg, and playwright in the project (bun add -d playwright; it uses Playwright's Chromium or an installed Google Chrome) for rendering and web capture. iOS Simulator capture needs macOS + Xcode (xcrun); Android capture needs adb (platform-tools).

Sizes (what each store wants)

size key pixels where
ios69 1320 x 2868 App Store 6.9" (APP_IPHONE_67), required
ios65 1284 x 2778 App Store 6.5" (APP_IPHONE_65)
ipad13 2064 x 2752 App Store 13" iPad (APP_IPAD_PRO_3GEN_129), required when the app runs on iPad; only with apple.ipad: true
play 1080 x 1920 Play phone (2-8, ratio at most 2:1)
tab7 1080 x 1920 Play 7" tablet (tablet frames, Android tablet body)
tab10 1440 x 2560 Play 10" tablet
feature 1024 x 500 Play feature graphic (required)

Sources (re-check before a release; Apple renames display classes often):

iPad. Set apple.ipad: true only if the app runs on iPad (iPhone-only apps skip the iPad set). Then the default sizes include ipad13 (3:4 layout), apple screenshots uploads it, validate requires it, and tablet frames default to the 13" iPad screen (1032 x 1352 CSS px @2x below a 24 px bar), so an iPad Pro 13-inch / iPad Air 13-inch simulator screenshot fits after the bar is cropped. Play tablet sizes (tab7 / tab10) reuse the same tablet frames, drawn in a plain Android tablet body instead of the iPad one.

Steps

  1. Game switches. Give the game URL params for screenshots: language, fixed seed, no ads / analytics / purchase UI, skip tutorial, jump to a level, freeze at a moment. Frames must show real gameplay (store rule).
  2. Capture. Configure shots.capture with a source (or pass --source), then storebound shots capture --lang en --dry to see the plan, and without --dry to capture. Frames land in <shots.frames>/<lang>/<phone|tablet>/<shot>.png, local, not committed.
    • web (default): baseUrl, query with {lang}, shots: path / waitFor / eval / delayMs / device. Phone 440 x 902 @3x and tablet 800 x 1240 @2x by default; the template draws the status bar above.
    • ios-sim (Unity, Godot, Flutter, native, Capacitor: any Simulator build): ios.device (simulator name or UDID, e.g. a 6.9" iPhone), ios.bundleId, optional ios.app (.app to install), ios.args. Per shot: args (launch arguments, read them with UserDefaults / your engine) and / or url (deep link). Language goes in as -AppleLanguages / -AppleLocale; status bar is forced to 9:41, full battery. After a cold boot the app is launched once and given ios.warmupMs (default 8000) before the first shot; without it the first frame of a fresh simulator is often blank. A simulator storebound boots is shut down afterwards (--keep-booted keeps it). For iPad shots set ios.devices.tablet (e.g. "iPad Pro 13-inch (M5)") and device: 'tablet' | 'both' per shot.
    • android-emu: android.package, optional serial, activity, apk. Per shot: url (VIEW intent) and / or typed extras. Demo-mode status bar; per-app language on Android 13+ (older: pass {lang} in an extra). Nothing is wiped or rebooted.
    • folder: frames made any other way; shots capture (and shots check) only verify that every frame the scenes use exists with the right aspect ratio and resolution. {lang}, {locale}, {shot} are replaced in args, extras and URLs. Simulator / emulator screenshots lose cropTop rows (the status bar) so they fit the template: by default the frame's bar share per device class (162 px on a 6.9" iPhone, 48 px on a 13" iPad with apple.ipad); set cropTop: 120 or { phone, tablet } for other devices, and shots.phone / shots.tablet to that device's screen in points. Capture warns when a frame's aspect does not match. Give the game time to load: delayMs (simulator / emulator; 2500 default is short for a cold start) or waitFor (web).
  3. Copy. store/shots/copy.json: per language, per scene: eyebrow, title [white line, gold line], sub, badges big, small. Short and true (only claim what is in the game); about 15 Latin characters per title line. Natural marketing tone per language, not literal translation; use the game's own terms.
  4. Fonts. storebound shots fonts downloads title/body fonts and a Noto subset for every non-Latin script found in the copy (CJK, Arabic, Hebrew, Devanagari, Thai, Cyrillic/Greek). Re-run after copy edits.
  5. Scenes. shots.scenes: palette (fire, violet, blast, green, night, sky, gold, candy or custom), frames (one front phone + optional back phones with x / dy / scale / rotate), badge slots (y, side, icon, hot, stars).
  6. Render. storebound shots render --lang en first, review store/renders/en/sheet.png (contact sheet, sizes packed into a grid), then all languages in the background: storebound shots render > .storebound/render.log 2>&1 &.
  7. JPEG for Play. storebound shots jpeg (q92, about 4x smaller than PNG; App Store stays PNG).
  8. Check. storebound shots check (read-only, exit 1 on errors): pixel sizes (Apple: a listed size, portrait or landscape; Play screenshots 320-3840 px at most 2:1; feature graphic exactly 1024 x 500), alpha, Play JPEG / 24-bit PNG and 8 MB, counts per set (Apple 1-10, Play 2-8), text overflow flags (warnings). --variants also checks A/B variant folders. storebound validate runs the same checks on the renders folder and lists errors as blockers next to the store state (--no-local skips them).
  9. Review. storebound shots gallery writes <renders>/index.html: one self-contained page (no network, open it from disk) with every language x size x scene, app previews with posters, variant tabs, filters (language, size, only issues, search) and light / dark. Tiles with store check errors are outlined red, warnings amber, with the message under the tile. Show the owner this page (or a screenshot of it) before uploading.
  10. Upload. storebound play images and storebound apple screenshots (both idempotent, background + log).

A/B variants and store experiments

shots.variants.<id>: scenes (order / subset), copy (a JSON merged over the base copy, only what changes), palettes (per scene). storebound shots render --variant b (or --variants for all) writes <renders>/b/<lang>/...; shots check --variants, shots gallery (tabs) and shots jpeg --variant b cover them. play images --variant b / apple screenshots --variant b upload a variant to the MAIN listing (replacing the base images there), so only do that after a test has a winner. What the stores offer, honestly:

  • Google Play store listing experiments (Play Console > Grow users > Store listing experiments): up to 2 variants against the current listing; one default-graphics experiment or up to 5 localized experiments at a time; icon, feature graphic, screenshots (+ descriptions for localized ones); stops automatically after 6 months. Console only: the Play Developer API has no experiments endpoint, so storebound renders and checks the variant images and you upload them in the experiment form yourself. https://support.google.com/googleplay/android-developer/answer/6227309
  • App Store Product Page Optimization (App Store Connect > your app > Product Page Optimization): up to 3 treatments against the original, one test at a time, up to 90 days; icon, screenshots and app previews, localized per treatment; new metadata goes through App Review; alternate icons must ship in the binary; shown on iOS / iPadOS 15+. https://developer.apple.com/app-store/product-page-optimization/ The App Store Connect API has the resources (appStoreVersionExperiments v2, treatments, treatment localizations with their own screenshot / preview sets), but storebound does not create or start experiments: set the test up in App Store Connect and upload the treatment screenshots there (the variant folder already has the right sizes).
  • Test one thing per experiment (usually the first screenshot or the order), give it real traffic, and do not read results early.

Gotchas

  • Output is 24-bit RGB PNG: Play rejects alpha / palette PNGs, Apple rejects alpha.
  • Arabic: rtl: true on the language; numbers stay LTR inside RTL text automatically.
  • Japanese breaks by phrase, Korean at spaces; long words shrink the line rather than wrap.
  • Check every language's sheet for overflow before uploading; German / Russian titles run long.
  • Play: 8 per type max; Apple: 10 per set max.
  • Do not include prices, "free" badges for paid items, or other platforms' UI.