The asset tree is the public swap interface for art. Designers and asset-generation agents must be able to replace any actor's visuals without touching code. This document defines the layout, the manifest contract, and the rules every asset-producing and asset-consuming agent must follow.
See
CLAUDE.md§2.3 for the mandate; this file is the contract.
assets/
heroes/<heroId>/ one folder per playable hero
enemies/<enemyId>/ one folder per enemy archetype
bosses/<bossId>/ one folder per boss
minibosses/<id>/ one folder per miniboss
player/ the controlled actor's frames + sheets
abilities/<abilityId>/ ability icons + effect art
boons/<boonId>/ boon icons + effect art (if any)
traits/<traitId>/ trait icons
tiles/<tileId>/ ground/wall/decor tiles
vfx/<vfxId>/ visual-effect sheets
ui/<componentId>/ UI sprites, 9-slice panels, buttons
Rules:
- One folder per "thing." If an enemy has 6 frames and a death sheet,
it lives in
assets/enemies/<id>/— not scattered across the tree. - Folder ids are kebab-case and stable. Renaming a folder is a contract change and requires a manifest update + a code reference audit.
- A
nanobanana-output/subfolder under any actor folder is reserved for raw generator output. The shipped asset is at the actor-folder root. Code never points intonanobanana-output/. - New top-level asset categories require a one-line addition to this file and to the manifest schema (§3).
assets/enemies/charger/
charger.png primary still / portrait
charger_idle.png (or sheet)
charger_walk.png
charger_attack.png
charger_hit.png
charger_death.png
manifest.json per-actor manifest (see §3)
nanobanana-output/ raw generator output (not shipped)
- All frame / sheet file names are prefixed with the folder id — this makes grep, finder search, and accidental-rename detection trivial.
- Sprite-sheets end in
_sheet.pngand ship alongside a_sheet.jsonwith frame UVs. - Audio (when introduced) sits in the same folder under
sfx/with the same id-prefix rule.
Code never hard-codes an asset path. Every actor folder ships a
manifest.json that declares its assets; the loader resolves these
at boot.
assets/enemies/charger/manifest.json:
{
"id": "charger",
"kind": "enemy",
"sprites": {
"idle": "charger_idle.png",
"walk": "charger_walk.png",
"attack": "charger_attack.png",
"hit": "charger_hit.png",
"death": "charger_death.png"
},
"sheets": {},
"sfx": {}
}And a top-level assets/manifest.json indexes them:
{
"enemies": ["charger", "bomber", "bug", "..."],
"heroes": ["knight", "mage", "..."],
"bosses": ["toad", "hornet", "..."]
}Consumer code:
// view/sprites.js — correct
import { loadActor } from './loader.js';
const charger = await loadActor('enemies', 'charger');
ctx.drawImage(charger.sprites.idle, x, y);// view/anywhere.js — FORBIDDEN
const img = new Image();
img.src = 'assets/enemies/charger/charger_idle.png'; // ❌ inline pathReplacing the art for any actor must require zero code changes. That means:
- The new files use the same filenames as the old (or the manifest is updated; the loader respects it either way).
- The folder id does not change.
- New sprites preserve the same logical anchor (feet-centred for ground actors, body-centred for flying, top-left for tiles).
- New sheets preserve the same frame count and order, or update
_sheet.jsonto match.
If a swap requires a code change, the swap was incorrect.
- Portraits, hero stills, boss reveals: portrait orientation (typically 3:4 or 9:16). Never landscape.
- In-game actor sprites: tight, transparent-background, feet at the bottom edge for ground units.
- UI sprites: design for the portrait HUD; no assumptions about landscape layouts.
| Asset task | Owner agent |
|---|---|
| Establish project palette / style anchor | style-establisher (one-time) |
| Per-actor sprite generation (≤128px) | game-sprite-author |
| Hero / boss portraits (≥512px) | character-portrait-designer |
| Ability / UI / inventory icons | icon-creator |
| UI mockups (HUD, menus, shops) | mockup-creator |
| Environment / level concept art | environment-concept-artist |
| Tileable terrain / surface textures | tileable-texture-generator |
| VFX sheets (smoke / fire / magic / sparks) | particle-vfx-sheetwriter |
| Logo / wordmark / app icon | logo-and-wordmark-designer |
| Background removal / cleanup | transparent-bg-extractor |
| Spritesheet packing | spritesheet-packer |
| Naming-convention enforcement | asset-naming-enforcer |
| Web-game asset import + manifest wiring | webgame-asset-importer |
| Style-drift review on batches | art-style-auditor |
Generation rules:
- Image generation routes through nano-banana via the Fortis
gateway (
scripts/gen-image-fortis.sh). Do not invoke Flux2. Do not expect a personalGEMINI_API_KEY. - New asset batches must be reviewed by
art-style-auditorbefore being marked shipped.
- Asset path string-literals in
src/outside the loader. - A
misc/ortemp/folder underassets/. - Mixed-id files inside one folder (e.g.
goblin_idle.pngsitting inassets/heroes/knight/). - A
assets/<id>.pngfloating at a category root, not inside its own folder. - New asset types added without a manifest schema bump.