Assets
All visual assets live in the assets/ directory, organized into categorized subdirectories. Sprites are PNG files loaded by raylib and sounds are WAV files in assets/sounds/, generated by tools/gen_sounds.py. Text uses raylib’s built-in font, so there is no font directory.
assets/
├── sounds/ ← Generated WAV files (see Sounds page)
│ ├── collectibles/
│ ├── entities/
│ ├── hazards/
│ ├── levels/
│ ├── player/
│ ├── screens/
│ └── surfaces/
└── sprites/ ← PNG sprite sheets and textures
├── backgrounds/ ← Parallax background layers
├── collectibles/ ← Coins, stars
├── entities/ ← Enemy sprites
├── foregrounds/ ← Fog, water, lava, clouds
├── hazards/ ← Spike, saw, flame sprites
├── levels/ ← Level tilesets and themes
├── player/ ← Player sprite sheet
├── screens/ ← HUD, menu assets
├── surfaces/ ← Platforms, bridges, vines, etc.
└── unused/ ← Reserve assets for future use
Coordinate note: All game objects use logical space (400x300). The raylib render target scales to the 800×600 OS window at 2×. A 48×48 sprite appears as 96×96 physical pixels on screen.
Per-Level Asset Configuration
Several visual elements are configurable per TOML level file, allowing different themes without changing code:
Floor Tile
floor_tile_path = "assets/sprites/levels/grass_tileset.png"
Any 48×48 PNG tile can be substituted here. The engine 9-slice renders it across FLOOR_Y to form the ground.
Parallax Background Layers
[[background_layers]]
path = "assets/sprites/backgrounds/sky_blue.png"
speed = 0.0
[[background_layers]]
path = "assets/sprites/backgrounds/glacial_mountains.png"
speed = 0.2
Layers are TOML array-of-tables and are drawn in array order (first = furthest back). speed is the parallax scroll factor: 0.0 = static sky, 1.0 = locks to camera, 0.1–0.5 = typical mid-ground parallax. Up to 8 layers are supported. See Level Design for the full list of available background images and suggested speeds.
Foreground Strip and Fog Layers
foreground_layers currently select the level foreground strip texture used by the water/lava renderer; the active runtime takes the last valid configured path. Atmospheric overlays are configured separately with fog_layers and loaded into the fog system.
[[foreground_layers]]
path = "assets/sprites/foregrounds/water.png"
speed = 0.0
[[fog_layers]]
path = "assets/sprites/foregrounds/fog_1.png"
[[fog_layers]]
path = "assets/sprites/foregrounds/fog_2.png"
Asset Reference
These tables include runtime resources and available theme variants; being bundled does not mean a current level references a file. Asset Inventory is the generated size/budget record. Asset Provenance records attribution and unresolved license evidence.
Backgrounds — assets/sprites/backgrounds/
| File | Used By | Description |
|---|---|---|
sky_blue.png |
levels/00_sandbox_01.toml, every lab |
Static sky gradient backdrop |
clouds_bg.png |
levels/00_sandbox_01.toml |
Background cloud layer |
glacial_mountains.png |
levels/00_sandbox_01.toml |
Distant mountains |
clouds_mg_3.png |
levels/00_sandbox_01.toml |
Midground cloud layer 3 |
clouds_mg_2.png |
levels/00_sandbox_01.toml |
Midground cloud layer 2 |
clouds_lonely.png |
levels/00_sandbox_01.toml |
Single cloud layer |
clouds_mg_1.png |
levels/00_sandbox_01.toml |
Foreground cloud layer |
sky_fire.png |
levels/01_lugio_01.toml, 02_lugio_02.toml |
Volcanic sky backdrop |
sky_fire_lightened.png |
Theme variant (no current level) | Lightened volcanic sky variant |
volcanic_mountains.png |
levels/01_lugio_01.toml, 02_lugio_02.toml |
Volcanic mountain layer |
volcanic_mountains_lightened.png |
Theme variant (no current level) | Lightened volcanic mountains |
smoke_bg.png, smoke_mg_1.png, smoke_mg_2.png, smoke_mg_3.png, smoke_lonely.png |
Volcanic levels | Smoke parallax layers |
smoke_mg_1_lightened.png |
Theme variant (no current level) | Lightened smoke midground |
forest_leafs.png |
levels/labs/06_camera.toml |
Forest leaf layer |
Foregrounds — assets/sprites/foregrounds/
| File | Used By | Description |
|---|---|---|
fog_1.png |
fog_layers in levels/00_sandbox_01.toml |
Fog overlay layer, semi-transparent sliding effect |
fog_2.png |
fog_layers (no current level) |
Fog overlay layer, semi-transparent sliding effect |
water.png |
foreground_layers / water->texture |
384x64 sprite sheet, 8 frames of 48x64 with 16x31 art crop |
lava.png |
foreground_layers / water->texture |
Lava-themed foreground strip |
clouds.png |
Editor foreground choice (no current level) | Decorative cloud layer |
fog_fire_1.png, fog_fire_2.png |
fog_layers in the volcanic levels |
Fire fog overlay layers |
smoke.png |
Theme variant (no current level) | Smoke overlay layer |
fog.c loads a level’s fog_layers paths into fog->textures[] in array order
(up to MAX_FOG_TEXTURES).
Player — assets/sprites/player/
| File | Used By | Description |
|---|---|---|
player.png |
player->texture |
192x288 sprite sheet, 4 cols x 6 rows, 48x48 frames |
Entities — assets/sprites/entities/
| File | Used By | Description |
|---|---|---|
spider.png |
gs->textures.spider |
Spider enemy sprite sheet (ground patrol) |
jumping_spider.png |
gs->textures.jumping_spider |
Jumping spider enemy sprite sheet |
bird.png |
gs->textures.bird |
Slow sine-wave bird enemy sprite sheet |
faster_bird.png |
gs->textures.faster_bird |
Fast aggressive bird enemy sprite sheet |
fish.png |
gs->textures.fish |
Jumping fish enemy sprite sheet |
faster_fish.png |
gs->textures.faster_fish |
Faster fish enemy sprite sheet |
Collectibles — assets/sprites/collectibles/
| File | Used By | Description |
|---|---|---|
coin.png |
gs->textures.coin |
16x16 coin collectible sprite |
star_yellow.png |
gs->textures.star_yellow |
Yellow star collectible sprite |
star_green.png |
gs->textures.star_green |
Green star collectible sprite |
star_red.png |
gs->textures.star_red |
Red star collectible sprite |
last_star.png |
gs->textures.last_star |
Last star goal collectible sprite |
Hazards — assets/sprites/hazards/
| File | Used By | Description |
|---|---|---|
spike.png |
gs->textures.spike |
Floor/ceiling spike hazard |
spike_block.png |
gs->textures.spike_block |
Rail-riding rotating hazard sprite |
spike_platform.png |
gs->textures.spike_platform |
Spiked platform hazard sprite |
circular_saw.png |
gs->textures.circular_saw |
Rotating saw blade hazard |
axe_trap.png |
gs->textures.axe_trap |
Swinging axe trap hazard |
blue_flame.png |
gs->textures.blue_flame |
Blue flame hazard sprite |
fire_flame.png |
gs->textures.fire_flame |
Fire flame hazard sprite |
Surfaces — assets/sprites/surfaces/
| File | Used By | Description |
|---|---|---|
float_platform.png |
gs->textures.float_platform |
48x16 sprite, 3-slice horizontal strip (left cap, centre fill, right cap) |
bridge.png |
gs->textures.bridge |
16x16 single-frame brick tile for crumble walkways |
bouncepad_small.png |
gs->textures.bouncepad_small |
Small bouncepad sprite (low launch) |
bouncepad_medium.png |
gs->textures.bouncepad_medium |
Medium bouncepad sprite (standard launch) |
bouncepad_high.png |
gs->textures.bouncepad_high |
High bouncepad sprite (max launch) |
rail.png |
gs->textures.rail |
64x64 sprite sheet, 4x4 grid of 16x16 bitmask rail tiles |
vine_green.png |
gs->textures.vine_green |
16x48 green climbable vine sprite |
vine_brown.png |
gs->textures.vine_brown |
16x48 brown climbable vine sprite |
ladder.png |
gs->textures.ladder |
Climbable ladder sprite |
rope.png |
gs->textures.rope |
Climbable rope sprite |
Levels — assets/sprites/levels/
| File | Used By | Description |
|---|---|---|
grass_tileset.png |
gs->textures.floor_tile |
48x48 tile, 9-slice rendered across FLOOR_Y to form the floor |
grass_platform.png |
gs->textures.platform |
Default 48x48 platform tile, 9-slice rendered as one-way platform pillars |
stone_tileset.png |
Volcanic levels (floor_tile_path) |
48x48 stone floor tileset |
stone_platform.png |
Volcanic levels (platform tile_path) |
Stone platform pillar tile |
grass_rock_tileset.png, grass_rock_platform.png |
Reserve / level theming | Grass-rock floor/platform variants |
brick_tileset.png, brick_platform.png |
Reserve / level theming | Brick floor/platform variants |
leaf_tileset.png, leaf_platform.png |
Reserve / level theming | Forest leaf floor/platform variants |
cloud_tileset.png |
Reserve / level theming | Cloud tileset |
Screens — assets/sprites/screens/
| File | Used By | Description |
|---|---|---|
hud_coins.png |
hud.c |
Coin count UI icon used in the HUD |
start_menu_logo.png |
start_menu.c |
Game logo displayed on the start menu screen |
Text font
The HUD, menus, overlays and editor draw text with raylib’s built-in default
font through src/shared/text.c. It is a 10 px bitmap font compiled into raylib
(zlib license), drawn at exactly TEXT_FONT_SIZE (10) with point filtering so
each font pixel maps to one canvas pixel. It covers ASCII and Latin-1;
other characters draw as ?. No font file ships with the game.
Player Sprite Sheet – player.png
Sheet dimensions: 192 x 288 px Grid: 4 columns x 6 rows Frame size: 48 x 48 px
Animation Row Map
| Row | AnimState | Frame Count | Frame Duration | Notes |
|---|---|---|---|---|
| 0 | ANIM_IDLE |
4 | 150 ms/frame | Subtle breathing cycle |
| 1 | ANIM_WALK |
4 | 100 ms/frame | Looping run cycle |
| 2 | ANIM_JUMP |
2 | 150 ms/frame | Rising phase poses |
| 3 | ANIM_FALL |
1 | 200 ms/frame | Descent pose |
| 4 | ANIM_CLIMB |
2 | 100 ms/frame | Vine climbing cycle |
| 5 | (unused) | – | – | Available for future states |
Frame Source Rect Formula
frame.x = anim_frame_index * FRAME_W; // column x 48
frame.y = ANIM_ROW[anim_state] * FRAME_H; // row x 48
Horizontal Flipping
When player->facing_left == 1, sprite_draw uses SPRITE_FLIP_X, expressed as a negative source width in raylib’s DrawTexturePro. The same right-facing animation frames serve both directions.
Unused Assets
The following asset is stored in assets/sprites/unused/ and is not loaded by the game. It is available as a reserve for future use.
| File | Description |
|---|---|
sky_background_0.png |
Sky gradient background |
Additional background variants are stored alongside used backgrounds in assets/sprites/backgrounds/; no current level references them, but they ship as playable theme options:
| File | Description |
|---|---|
castle_pillars.png |
Castle/dungeon pillar background |
clouds_mg_1_lightened.png |
Lightened midground cloud variant |
glacial_mountains_lightened.png |
Lightened mountain variant |
sky_blue_lightened.png |
Lightened sky variant |
sky_fire_lightened.png, volcanic_mountains_lightened.png, smoke_mg_1_lightened.png |
Lightened volcanic variants |
Additional tilesets are stored in assets/sprites/levels/; the stone variants
are already used by the volcanic campaign levels:
| File | Description |
|---|---|
brick_platform.png |
Brick platform tile |
brick_tileset.png |
Brick wall / platform tile |
cloud_tileset.png |
Cloud platform tile |
grass_rock_platform.png |
Grass + rock platform tile |
grass_rock_tileset.png |
Grass + rock mixed tileset |
leaf_platform.png |
Leaf / foliage platform tile |
leaf_tileset.png |
Leaf / foliage platform tile |
stone_platform.png |
Stone platform tile |
stone_tileset.png |
Stone floor / wall tile |
Sprite Sheet Analysis
To inspect any sprite sheet’s exact dimensions and pixel layout:
python3 tools/analyze_sprite.py assets/sprites/<category>/<sprite>.png
The tool requires Python and Pillow. Supply explicit frame dimensions when known, for example:
python3 tools/analyze_sprite.py assets/sprites/entities/spider.png 64 48
Without explicit dimensions, the analyzer looks for transparent gutters. Verify its result against the image: sheet width and height must divide into whole frame columns and rows. Frames are zero-indexed; transparent padding is not part of the visible art or collision hitbox. Measure the art bounds before setting ART_X/Y/W/H or hitbox padding, and check rows separately when their usable frame counts differ.
Theme Variants
Match an existing asset’s dimensions, frame grid, and silhouette before making a theme variant. Palette-only variants preserve pixel positions and transparency; use a limited palette and inspect silhouettes at game scale. Check 9-slice tilesets tiled together for seams.
tools/gen_fire_sprites.py retains the fire-palette generation utility:
python3 tools/gen_fire_sprites.py
It also requires Pillow and resolves assets relative to the repository, not the working directory. Running it writes 13 background/foreground PNGs under assets/sprites/; run it only when intentionally regenerating those assets and review the resulting diff.
Frame Math Reference
Sheet width = cols x frame_w
Sheet height = rows x frame_h
source_x = (frame_index % cols) * frame_w
source_y = (frame_index / cols) * frame_h
Generated Sounds
Every WAV under assets/sounds/ is synthesized by tools/gen_sounds.py, which
needs only the Python standard library. The file doubles as a short synthesis
lesson: oscillators with a phase accumulator, exponential pitch sweeps, ADSR
and exponential envelopes, seeded noise, one-pole low/high/band-pass filters,
peak normalization and seamless loop crossfades.
make sounds # rewrite WAVs that changed beyond 1 LSB
python3 tools/gen_sounds.py --check # exit 1 if a committed WAV drifts
Output is mono 16-bit PCM at 22050 Hz and byte-identical on every run on the
same platform, because each recipe draws noise from its own fixed
random.Random seed. make docs-drift runs --check, which requires
identical WAV headers and frame counts but lets each sample differ by 1 LSB,
because math.sin/math.exp come from the platform C library and may round
differently on macOS and Linux. make sounds applies the same tolerance and
reports such files as unchanged instead of rewriting them, so regenerating on
another OS only touches sounds whose recipe actually changed. --check
also rejects WAV files the generator does not produce; make test runs tests/gen_sounds_test.py (format, determinism, fade
outs, loop seams, the 1-LSB tolerance, unchanged-file skips and a 2 MiB sound budget). To change a sound, edit its recipe
and seed/peak entry in the SOUNDS table, run make sounds, then listen and
commit the regenerated files with the recipe. To add one, add a recipe and
table row first; the C side then loads it as described on the
Sounds page.
Loading an Asset
// In game_init or an entity's init function:
Texture2D *tex = texture_load("assets/sprites/collectibles/coin.png");
if (!tex) {
fprintf(stderr, "Failed to load assets/sprites/collectibles/coin.png\n");
return -1;
}
// At cleanup (reverse init order):
texture_unload(tex); tex = NULL;