Store-ready screenshots — App Store, Google Play, Chrome Web Store — generated by your AI agent.
shotframe builds every asset as real HTML/CSS and screenshots it in headless Chromium
at the exact pixel size each store requires. Fonts are bundled, so the same config
produces the same image on macOS/Linux/Windows. You don't hand-craft the assets:
shotframe init installs an agent skill into your repo, and from then on you just
ask your coding agent for store screenshots — it studies your app, redraws the key
screens, writes the captions, renders, looks at the images, and iterates.
Every image below was generated by an AI agent straight from the app's own source — no screenshots taken, no assets hand-placed. Each screen is redrawn as HTML, captioned, and rendered at its store's exact pixel size. (So is the banner at the top of this README — it's a shotframe render too.)
Spik — App Store 6.9" · learn English by speaking (bilingual feed → score → progress)
iGourmet — App Store 6.9" · Vietnamese restaurant-group loyalty & wallet
AI Solve Quiz — Chrome Web Store 1280×800 · snap any question, read the AI answer
Every label is real app copy: iGourmet renders entirely in Vietnamese (đ ơ ư ạ ề ố, ₫)
from the bundled fonts, Spik stays bilingual English/Vietnamese. The hero banner and the
Chrome Web Store shot are both reproducible from examples/showcase;
the two phone sets live in each app's own repo.
shotframe is not on the npm registry — install the self-contained tarball from GitHub Releases:
npm i -g https://github.com/crisng95/shotframe/releases/download/v1.0.0/shotframe-1.0.0.tgz
npx playwright install chromium # one-time; rendering runs in headless ChromiumFrom source
git clone https://github.com/crisng95/shotframe && cd shotframe
pnpm install && pnpm -r build && npx playwright install chromium
ln -sf "$PWD/packages/cli/dist/index.js" /usr/local/bin/shotframecd your-app
shotframe initinit scaffolds shotframe.config.ts and writes the agent skill to
.claude/skills/shotframe/SKILL.md. Then ask your agent (Claude Code or compatible):
"generate store screenshots for this app"
The agent takes it from there:
- Reads your project — name, value proposition, brand colors, key screens, the app's real UI strings (including i18n files, in the store's language).
- Redraws the 3–6 screens that sell the app as HTML presets — no screenshots, no running the app — and writes ASO captions ordered as a story: hook → core value → proof → CTA.
- Renders, looks at the output images, and iterates against a design checklist until they pass.
Output lands at <output.dir>/<store>/<id>.png|jpeg — one file per target, each at the
store's exact pixel size, reproducible byte-for-byte.
Have real screenshots? Point a target's source: at the image and shotframe
cover-fits it into a CSS device bezel, browser chrome, or your own frame PNG — the
skill handles both paths.
Not using Claude Code? AGENTS.md is the same playbook for any AI
tool. Prefer to hand-edit? The full config reference is
docs/config-schema.md, and
examples/basic/ is a complete worked example (config + HTML
presets).
| Command | What it does |
|---|---|
shotframe init |
Scaffold config + sample + agent skill. --json config, --no-skill, --force. |
shotframe skill |
(Re)write just the agent skill in an existing project. |
shotframe |
Render all targets (= shotframe render, the default command). Filter: -t <ids...>, -s <store>; -c <config>, -o <outdir>. |
shotframe list |
Print every target id + size, grouped by store. |
shotframe studio |
Live browser editor at 127.0.0.1:5179 for visual preview/tweaks. Long-lived server — for humans, not headless agents. |
Nothing is interactive: everything is declared in the config, config auto-detected from
cwd, exit 0 on success, non-zero with a one-line stderr reason on failure.
One target = one output image; a store needing N screenshots = N targets.
| Store | Target id(s) | Size (px) | Default frame | Default output |
|---|---|---|---|---|
appstore |
iphone69 |
1290 × 2796 | device bezel (island) | JPEG q0.94 |
appstore |
ipad13 |
2064 × 2752 | device bezel (none) | JPEG q0.94 |
play |
phone |
1080 × 1920 | device bezel (hole) | JPEG q0.94 |
play |
tablet |
1600 × 2560 | device bezel (hole) | JPEG q0.94 |
play |
feature-graphic |
1024 × 500 | none | JPEG q0.94 |
chrome |
screenshot-1…5 |
1280 × 800 | browser chrome | PNG |
chrome |
small-promo |
440 × 280 | none | PNG |
chrome |
marquee |
1400 × 560 | none | PNG |
email |
preview / wide |
1200 × 1600 / 1600 × 900 | none | PNG |
Any custom size works too (e.g. a 1200 × 630 OG image): declare an email-store target
with your own size.
shotframe bundles its webfonts (@shotframe/fonts, woff2 subset to
latin + latin-ext + vietnamese) and loads the selected family before rendering, so text
never depends on host-installed fonts. Pick by name via brand.font (default Inter):
- Latin + Vietnamese:
Inter·Roboto·Montserrat·Plus Jakarta Sans·Space Grotesk - Latin only (upstream ships no Vietnamese glyphs):
Poppins·DM Sans·Sora
All OFL-1.1 (licenses; per-file coverage in
packages/fonts/fonts/coverage.json). Need another script or your exact brand font?
brand.fontFace: { family, src } loads your own woff2/ttf — use a family name that
doesn't collide with a bundled one.
@shotframe/core exposes one pure function — renderAsset(config, target): string —
composing background → device frame → screen → caption into a single #asset HTML
tree at exact device pixels. The CLI screenshots that element in headless Chromium; the
studio mounts the very same HTML live, so the preview is the file. A screen is filled
one of two ways:
- Path A — real screenshot:
source:image cover-fit into the frame (canvasCSS bezel ·browserchrome ·imageyour own PNG ·nonebare). - Path B — synthetic preset: an HTML-returning function the agent writes
(
preset:), styled with normal CSS against the injected brand tokens, fonts, and UI kit. This is what the skill does — and why no screenshots are needed.
| Symptom (stderr) | Fix |
|---|---|
Could not launch Chromium … |
npx playwright install chromium |
Config not found: <path> |
run from the project dir, or pass --config |
Source image not found: <ref> |
fix the path — source: paths are config-relative |
| zod validation error with a field path | correct that field per docs/config-schema.md |
All packages ship inside the release tarball — nothing to install separately.
| Package | What it is |
|---|---|
@shotframe/core |
Brand-free Full-DOM HTML engine (renderAsset). Zero runtime deps, isomorphic. |
@shotframe/config |
Zod schema, defineConfig, config loader, built-in store packs. |
@shotframe/cli |
shotframe CLI + agent skill writer. Bundles the studio UI. |
@shotframe/fonts |
Bundled OFL webfonts (woff2) for cross-OS determinism. |
@shotframe/studio |
Vite browser editor (private; bundled inside the CLI). |
pnpm workspace; requires Node 20.6+. Verify with exiting commands only (never leave a server running):
pnpm install
pnpm -r build && pnpm -r typecheck && pnpm -r test
node examples/basic/gen-shots.mjs # regenerate sample inputs (gitignored)
node packages/cli/dist/index.js render --config examples/basic/shotframe.config.ts --out /tmp/out- The engine is brand-free: no hex color literals or brand strings in
packages/core/src(enforced by a test). All colors/fonts/copy arrive via the config. - Releases use changesets for versioning +
git tags only — no npm publish. Distribution is the GitHub Release tarball built by
pnpm --filter @shotframe/cli pack:release.
MIT © 2026 ISEMI. Bundled fonts keep their upstream OFL-1.1 licenses.









