---
name: game-asset-naming
source: https://app.decimal.ai/s/game-asset-naming@1/SKILL.md
source_sha256: c7c9d6f80252
---

# Game asset naming & format convention

## Contract

Every game asset file (texture, audio, data) gets a filename built from a fixed,
category-first token grammar with a context-specific file format. Apply it whenever you
are asked to name, save, or rename an asset file — produce one exact filename, never an
ad-hoc or descriptive name.

## Rules

### Texture / art files

Pattern, exact order, underscore-separated:

```
category_name_variant_size.ext
```

1. **`category`** — FIRST token, one of exactly these six: `character`, `prop`,
   `environment`, `ui`, `fx`, `tile`. No synonyms (`char`, `enemy`, `bg`, `vfx`,
   `terrain`, `icon` are all wrong — map them to the canonical token).
2. **`name`** — the lowercase subject of the asset: `goblin`, `sword`, `crate`,
   `closebutton`. A single token, no internal separator.
3. **`variant`** — the visual variant: `red`, `damaged`, `night`, `pressed`, `gold`.
   If the asset has no variant, use the literal token `base` — never omit the slot.
4. **`size`** — the texture's pixel dimension as a power-of-two INTEGER, one of:
   `64`, `128`, `256`, `512`, `1024`, `2048`, `4096`. Never a word (`small`, `large`,
   `hi`, `sm`), never a non-power-of-two, never `512x512` (just `512`). Textures are
   square, so one number covers both dimensions.
5. **`.ext`** — chosen by destination, not by source format:
   - UI / 2D-overlay / HUD textures → `.png`
   - 3D / model / world / level textures → `.ktx2` (compressed)
   - Never `.jpg`, `.jpeg`, `.tga`, `.bmp`, `.psd` for shipped textures.

### Audio files

Pattern, exact order — note `context` is SECOND and there is **no size token**:

```
category_context_name_variant.ext
```

1. **`category`** — FIRST token, one of exactly: `sfx`, `music`, `voice`, `ambient`.
2. **`context`** — SECOND token, the gameplay context: `combat`, `ui`, `world`, `menu`.
3. **`name`** — the lowercase subject: `sword_swing` becomes the `name` (+ optional
   `variant`) portion; e.g. `swing`, `click`, `footsteps`, `title`.
4. **`variant`** — optional trailing variant (`loop`, `alt`, `heavy`). Omit if none —
   audio does NOT require a `base` filler the way textures do.
5. **`.ext`** — always `.ogg`, for every audio category including music. Never `.wav`,
   `.mp3`, `.aiff`, `.flac`.

### Data files

```
snake_case_name.json
```

- snake_case lowercase words joined by underscores, `.json` extension.
- Example: `enemy_stats.json`, `loot_table.json`. Never `.yaml`/`.yml`/`.xml` for these,
  never `enemyStats.json` or `enemy-stats.json`.

### Universal rules (all asset types)

- All lowercase. Always.
- Separators are underscores ONLY — no spaces, no hyphens, no camelCase, no PascalCase,
  no dots except the single extension dot.
- Texture dimensions are square and power-of-two; the size token is the integer edge length.

## Worked examples (BEFORE = bare default, AFTER = conforming)

**Texture, character with variant, 3D model, 512px**
- BEFORE: `GoblinRed.png`  /  `goblin_red_small.png`  /  `goblin-red-512x512.tga`
- AFTER: `character_goblin_red_512.ktx2`
- Why: category-first (`character`), variant kept (`red`), integer power-of-two size
  (`512`), 3D destination → `.ktx2`.

**Texture, no variant → literal `base`, 3D prop, 1024px**
- BEFORE: `crate_1024.png` (variant slot dropped, wrong ext)
- AFTER: `prop_crate_base_1024.ktx2`
- Why: missing variant becomes the literal `base`; 3D → `.ktx2`.

**Texture, UI element, 1024px**
- BEFORE: `closeButton_pressed.png`  /  `ui_close_button_large.png`
- AFTER: `ui_closebutton_pressed_1024.png`
- Why: category `ui`, single-token name, real variant `pressed`, integer size, UI → `.png`.

**Texture, fx in a 2D UI overlay, 128px**
- BEFORE: `explosion_spritesheet.png` (no category, no size)
- AFTER: `fx_explosion_base_128.png`
- Why: category `fx`, `base` filler since "sprite-sheet" is not a visual variant, UI
  overlay → `.png`.

**Texture, environment, 2D menu backdrop, 4096px, night**
- BEFORE: `forest_night_bg.jpg`
- AFTER: `environment_forest_night_4096.png`
- Why: `bg`→`environment`, variant `night`, flat menu/UI use → `.png`, not `.jpg`.

**Audio, sfx in combat**
- BEFORE: `SwordSwing.wav`  /  `sword_swing_sfx.mp3`
- AFTER: `sfx_combat_sword_swing.ogg`
- Why: category-first `sfx`, context SECOND `combat`, no size token, `.ogg`.

**Audio, looping menu music**
- BEFORE: `title_theme.mp3`  /  `music_title.wav`
- AFTER: `music_menu_title_loop.ogg`
- Why: `music`, context `menu`, `loop` as the variant, `.ogg`.

**Audio, voice line, town menu**
- BEFORE: `merchant_greeting.wav`
- AFTER: `voice_menu_merchant_greeting.ogg`

**Audio, ambient world loop**
- BEFORE: `wind_ambience.ogg` (missing category/context order)
- AFTER: `ambient_world_wind_loop.ogg`

**Data file**
- BEFORE: `EnemyStats.yaml`  /  `enemy-stats.json`
- AFTER: `enemy_stats.json`

## Edge cases & exceptions

- **No variant on a texture** → use the literal word `base` (`ui_healthbar_base_256.png`).
  Do not drop the slot; the four-token shape is fixed.
- **No variant on audio** → simply omit it (`sfx_ui_click.ogg`). Audio has no `base`
  filler; the minimum is `category_context_name`.
- **"fx" / "vfx" / "particle"** all map to the texture category `fx` (when it is a
  texture). A particle texture used on a 3D world model is still `.ktx2`; one used as a
  2D UI overlay is `.png`. Destination decides the extension, not the word "fx".
- **A `tile`** is its own category, separate from `environment`. Ground/terrain tiles use
  `tile`; large background/level art uses `environment`.
- **Format is by destination, not by what was authored.** A texture exported as a PNG but
  destined for a 3D model still ships as `.ktx2`; the filename reflects the destination.
- **Multi-word subjects** collapse into one lowercase name token: "close button" →
  `closebutton`, "health bar" → `healthbar`. Don't introduce an extra underscore that
  would shift the token positions.
- **Albedo / normal / diffuse maps** are still just textures of their model's category
  (e.g. a dragon boss albedo → `character_dragon_base_2048.ktx2`); the map type is not a
  token in this grammar.

## Do / Don't

- DO put `category` first, always. DON'T lead with the subject name.
- DO use the canonical category vocabulary. DON'T invent `char`, `enemy`, `bg`, `vfx`,
  `terrain`, `icon`.
- DO use an integer power-of-two size token for textures. DON'T use `small`/`large`/`hi`
  or `512x512`.
- DO insert the literal `base` when a texture has no variant. DON'T drop the variant slot.
- DO pick the extension by destination: UI→`.png`, 3D→`.ktx2`, audio→`.ogg`. DON'T keep
  the source format (`.psd`, `.wav`, `.mp3`, `.tga`, `.jpg`).
- DO put audio `context` second with NO size token. DON'T copy the texture shape onto audio.
- DO use lowercase + underscores only. DON'T use spaces, hyphens, camelCase, or PascalCase.

## Common mistakes (the bare model's wrong defaults)

1. CamelCase / PascalCase descriptive names (`GoblinRed.png`) instead of token grammar.
2. Subject-first ordering (`goblin_character_...`) instead of category-first.
3. Word sizes (`_small`, `_large`, `_hi`) instead of the integer power-of-two.
4. Dropping the `base` variant slot on textures with no variant.
5. Keeping the authoring/source extension (`.psd`, `.tga`, `.jpg`, `.wav`, `.mp3`) instead
   of the destination format.
6. Using `.png` for a 3D texture (should be `.ktx2`) or `.ktx2` for a UI texture (should
   be `.png`).
7. Putting a size token on audio, or audio `context` in the wrong slot.
8. Hyphens or spaces as separators.

## Quick checklist

- [ ] Category token first, from the fixed vocabulary?
- [ ] Texture: `category_name_variant_size.ext`, integer power-of-two size, `base` if no variant?
- [ ] Audio: `category_context_name[_variant].ogg`, context second, no size token?
- [ ] Extension by destination — UI `.png`, 3D `.ktx2`, audio `.ogg`, data `.json`?
- [ ] All lowercase, underscores only, no source-format extension?
