Reference Notes
2026-11-04
The idea
The practice
The lab
| Family | What it includes | Job in the game | Typical format |
|---|---|---|---|
| 2D art | sprites, sheets, tilesets, backgrounds | the visible world | PNG |
| 3D art | meshes, materials, texture maps, props | the visible world, with depth | glTF / GLB, FBX |
| Animation | sprite frames, skeletal clips, blends | motion and feedback | PNG sheets, GLB clips |
| Audio | SFX, ambience, music, voice | feedback and tone | OGG, WAV, MP3 |
| Text | fonts, dialogue, localization | legibility and voice | TTF/OTF, JSON, CSV |
| Data | levels, tilemaps, atlases, configs | structure and content | JSON, YAML, TMX |
| Shaders + video | effects, cutscenes, menus | look and spectacle | GLSL/WGSL, WebM |
An asset is a file the game loads — not a feature you implement. That is what makes assets replaceable, and what makes a pipeline worth building.
1 · Find
search, tags, license filter
2 · License
credit? share-alike?
3 · Inspect
open the zip, measure the grid
4 · Organize
source vs runtime, credits
5 · Import
load once, at startup
6 · Configure
anchor, scale, filtering
7 · Integrate
animate, tile, mix
8 · Playtest
does it read? does it fit?
Playtest sends you back to Find: a pipeline is a loop. Every step has a failure mode — the wrong license, a missing file, the wrong grid, the wrong path, the wrong scale.
| License | Credit? | Share-alike? | What it means for your game |
|---|---|---|---|
| CC0 | no | no | Do anything. Credit as a courtesy. |
| CC-BY 3.0 / 4.0 | yes | no | Credit, link, and say what you changed. |
| CC-BY-SA 3.0 / 4.0 | yes | yes | Your changes to the art stay CC-BY-SA. |
| OGA-BY 3.0 / 4.0 | yes | no | CC-BY without the anti-DRM clause. |
| GPL 2.0 / 3.0 | yes | yes | Avoid unless the whole project is GPL. |
Record five things for every asset: title, author, license, URL, and what you changed.
OGA’s recommended credit line:
“Asset Name” by Author, licensed CC-BY 3.0: https://opengameart.org/content/…
CREDITS.md and on a credits screen.Ask of every pack:
.db files, nested zips, duplicate exports?.blend, .psd, .wav) and which are runtime (.glb, .png, .ogg)?A pack is a box of parts, not a game. You choose the subset; leave the rest out of the repo.
.blend, ship a .glb; edit a .wav, ship an .ogg.public/ is served at the site root: public/assets/x.png becomes assets/x.png in your code.player-run.png, not p1_spritesheet (1).png.| Grid sheet | Atlas | |
|---|---|---|
| Frame rects | computed from a cell size | listed explicitly |
| Ships with | nothing extra | a sidecar file |
| Example | a tileset: 70×70 cells, 2 px margin | Kenney’s player sheet: a .txt of rects |
A grid sheet is arithmetic; an atlas is data. Sidecars vary: TexturePacker JSON, Starling XML, Tiled TMX — and encodings vary too (Kenney’s p1_walk.json is UTF-16, so strip the BOM before JSON.parse).
A model is not one file:
Formats: glTF/GLB is the web format; FBX is the DCC interchange; OBJ is geometry only. Scale and units are a contract — glTF is metres, Y-up; FBX is often centimetres, Z-up.
| Role | Length | Count | Job |
|---|---|---|---|
| SFX | 50 ms – 2 s | dozens | feedback: the hit, the jump, the pickup |
| UI | 20 – 200 ms | a handful | confirmation: the click, the hover |
| Ambience | 10 s – 2 min loop | one per area | place: wind, crowd, dripping water |
| Music | 1 – 4 min loop | one per scene | tone and pacing |
| Voice | 1 – 30 s | story-dependent | character and story |
const sheetImage = new ex.ImageSource('assets/sprites/player.png');
const loader = new ex.Loader([sheetImage]);
const game = new ex.Engine({ width: 960, height: 540, pixelArt: true });
game.start(loader).then(() => {
const sheet = ex.SpriteSheet.fromImageSource({
image: sheetImage,
grid: { rows: 4, columns: 4, spriteWidth: 32, spriteHeight: 32 },
});
const run = ex.Animation.fromSpriteSheet(
sheet, [0, 1, 2, 3], 100, ex.AnimationStrategy.Loop
);
player.graphics.use(run, { anchor: ex.vec(0.5, 1) });
});getSprite(x, y) is (column, row); Animation.fromSpriteSheet takes row-major indices. Irregular atlas? Build the frames: new ex.Animation({ frames: sprites.map(g => ({ graphic: g })), frameDuration: 100 }).
const tiles = ex.SpriteSheet.fromImageSource({
image: tilesImage,
grid: { rows: 13, columns: 12, spriteWidth: 70, spriteHeight: 70 },
spacing: { margin: { x: 2, y: 2 } },
});
const map = new ex.TileMap({
tileWidth: 70, tileHeight: 70, rows: 15, columns: 40,
});
for (let y = 0; y < level.length; y++) {
for (let x = 0; x < level[y].length; x++) {
const i = level[y][x];
const tile = map.getTile(x, y);
if (!tile || i === 0) continue;
tile.addGraphic(tiles.getSprite(i % 12, Math.floor(i / 12)));
tile.solid = SOLIDS.has(i);
}
}
scene.add(map);
const jump = new ex.Sound('assets/audio/jump.ogg', 'assets/audio/jump.wav');
jump.volume = 0.6;
jump.play();The level is data; the tileset is art. i → (column, row) is % and Math.floor.
Do this
public/assets/.ex.Loader; replace the placeholder player with an animated sprite.ex.TileMap.CREDITS.md with the exact license lines.Done means
pnpm dev and pnpm build both workCommon failures: wrong grid → sliced sprites · wrong path → invisible actor · wrong anchor → floating player · no gesture → silence · missing credit → an academic honesty problem, not a style note.
GD 2000 · Game Assets — Reference