Assets
Sprite sheets, bitmap fonts, and asset loading.
Looking for audio assets? AudioClip loading is documented separately in API: Audio.
Asset size limits
Sprite sheets, font atlases, and raw indexed buffers share the same decoded-size policy as render configuration (8192
pixels per side, 16,777,216 total pixels). Limits are enforced before canvas readback, CPU buffer retention, GPU
texture creation, and software sprite loops.
| Limit | Default | Applies to |
|---|---|---|
| Max width / height | 8192 | Decoded PNGs, font atlas textures, fromIndexedPixels() |
| Max total pixels | 16,777,216 (4096×4096) | Same sources |
Max .btfont JSON size | 1,048,576 bytes (1 MiB) | BitmapFont.load() before JSON.parse() |
| Max embedded texture payload | 524,288 bytes (512 KiB) | Base64 data in texture when using data:image/png;base64,... |
| Max glyph count | 8192 | Glyph map entries in a .btfont file |
| Max software blit area | 16,777,216 pixels | Software renderer source rectangles (clipped to the sheet first) |
What happens on overflow
When a limit is exceeded, loading throws an AssetLimitError with a beginner-friendly message. The software renderer
skips sprite blits whose source rectangle is empty, non-integer, fully outside the sheet, or still too large after
clipping.
.btfontfiles may reference either a relative PNG path or an embedded PNG data URI.- Embedded textures must use
data:image/png;base64,...and stay within the embedded payload cap above. - Other
data:schemes (for example JPEG) are rejected before image decode. - Decoded atlas dimensions use the same width, height, and pixel-area limits as sprite sheets.
- Prefer separate PNG files for large atlases so the JSON payload stays under the JSON size limit.
Loading assets
AssetLoader Since 0.1.0
AssetLoader caches images by URL so repeated loads share the same HTMLImageElement. Oversized images are rejected as
soon as the browser reports decoded dimensions.
import { } from 'blit386';
// Load a single image (cached by URL)
const = await .('sprites.png');
// Load multiple images in parallel
const = await .(['sprites.png', 'tiles.png']);
// Check cache before loading
if (.('sprites.png')) {
// already cached
}
// Drop a single URL's cache entry (manual reload or tests; blit386/vite watcher usually skips this)
.('sprites.png');
// Number of image loads currently in flight
.;AssetLoader.evict() is also the manual escape hatch for forcing a fresh load outside the blit386/vite plugin's
automatic asset watcher - see Hot Reload.
BT.loadingAssetsCount Since 1.4.0
For a loading-screen indicator that covers both images and audio clips, use BT.loadingAssetsCount - it sums
AssetLoader.loadingCount and AudioClip.loadingCount (see API: Audio) and drops back to 0
once every in-flight load has settled:
if (. > 0) {
// show a spinner or progress bar
}Sprite setup - preferred path
SpriteSheet Since 0.1.0
IndexedSpriteLoadResult Since 1.0.3
Use SpriteSheet.loadIndexed() for all standard sprite setup. It combines color registration, image loading, and
palette indexization in one call.
import { , , , } from 'blit386';
const = new (256);
const = await .(
'sprites/hero.png', // URL
, // palette to populate
10, // startSlot - first palette slot to write colors into
{ : 'luminance' }, // optional: color order in palette ('luminance' | 'none')
);
.(); // activate AFTER loadIndexed returns
// Draw using the returned sheet and source rectangle:
.(., ., new (20, 20));
// indexed.colors - list of Color32 values registered into the palette
// indexed.srcRect - Rect2i spanning the full image- Colors are sorted by perceived luminance (darkest-first) by default.
- Pass
{ sort: 'none' }to preserve row-major scan order. - Slot
0is never touched - transparent pixels in the image map to slot 0 at draw time.
Tile grids
Pass tileSize to loadIndexed (or load) when the image is a grid of equal cells. BT.drawTile and sheet.tileRect
then find cells for you:
const { } = await .('/sprites/walk.png', , 1, { : new (16, 24) });
const = [0, 1, 2, 3].(() => .()); // built once in init()
. = 8; // or set or change it later; null removes the gridSetting sheet.tileSize (or the tileSize load option) to anything but a positive whole number of at most 8192 (the
largest sheet the engine accepts) on both axes throws a RangeError and leaves the grid unchanged. sheet.tileSize
reads back as a frozen Vector2i (or null). The grid survives a hot-reload image swap, and lookups are checked
against the sheet's current size. See Drawing tiles.
Sprite setup - manual path
Use this only when you need fine-grained control over the palette layout or want to load several sheets into the same palette sequentially.
import { , , } from 'blit386';
const = new (256);Register colors into the palette
Each call appends the image's colors starting at the given slot; chain them with running offsets to pack several sheets into one palette.
const = await .('hero.png', , 10);
const = await .('tiles.png', , 10 + .);Load the image into a sprite sheet
const = await .('hero.png');Convert RGBA pixels to palette indices
indexize() maps every pixel to its palette slot; activate the palette afterward.
.();
.();To create a sheet from raw palette-indexed pixel data (advanced / test use):
const = .(, , );Palette offset
Pass a paletteOffset (per-draw shift, not an absolute slot) to BT.drawSprite() and BT.printFont() to remap stored
texel indices before palette lookup. Useful for team-color variations and damage flashes. Terminology and examples:
Palette addressing. Draw semantics: API: Rendering.
.(, , , 16); // render in "blue team" color rangeBitmap fonts
BitmapFont Since 0.1.0
Load .btfont files for proportional, palette-indexed bitmap fonts. After loading, register colors in the palette and
indexize the font's internal sprite sheet before drawing (same pattern as manual sprite setup).
const = new (256);
const = await .('fonts/MyFont.btfont');
.().();
.();
.(, new (10, 10), 'Hello!');
.(, new (10, 10), 'Hello!', ); // per-draw index shift- Font rendering goes through the same sprite pipeline as
BT.drawSprite()and is auto-batched. - SpriteSheet helpers (after indexize):
isIndexed(),getIndexedPixels()(defensive copy),getIndexedPixelsRef()(no copy, for hot-path CPU blitting - do not mutate the returned array),reindexize(palette)when palette layout changes.SpriteSheet.loadIndexed()returnsIndexedSpriteLoadResult({ sheet, srcRect, colors }). - See Bitmap Fonts Guide for the
.btfontformat specification and the BMFont conversion workflow (pnpm run convert-font).
Hot-replacing assets
Under a Vite dev server with the blit386/vite plugin installed, editing a sprite sheet's source image or a .btfont
file under a watched asset directory (public/ by default) updates the already-loaded asset in place - no page reload,
and demo-held references (a SpriteSheet or BitmapFont instance, or an IndexedSpriteLoadResult.sheet) stay valid. A
sprite sheet re-runs indexize() against the active palette when it was already indexized; a bitmap font rebuilds its
glyph tables and texture.
Demo-held srcRects and dimension changes
If the replacement image has different dimensions, the sheet's size updates to match, but any Rect2i a demo is
holding onto as a srcRect into that sheet does not update itself - reconciling it is the demo's own
responsibility. Keep sprite sheet dimensions stable during a hot-reload session, or recompute srcRects from the
sheet's current width/height when that matters.
Each SpriteSheet also exposes status ('loading' | 'ready' | 'failed') and progress (0 or 1) tracking a
replacement fetch while it's in flight - useful for a per-sheet loading indicator, or combine with
BT.loadingAssetsCount for a single engine-wide signal. A normally loaded sheet is always 'ready'
with progress: 1.0, since its image has already resolved by the time the sheet is constructed.
.; // 'loading' | 'ready' | 'failed'
.; // 0 while loading, 1.0 once readyCoarse-grained progress
progress is 0 or 1.0, never a value in between - HTMLImageElement reports no byte-level download progress the
way AudioClip's onProgress callback does (see API: Audio). Use status/progress to show
or hide a loading indicator, not to drive a percentage bar.
See Hot Reload for the full asset type matrix, including audio and the fallback-to-full-reload behavior for unrecognized file types.
System font
A built-in 6×14 monospace font covering printable ASCII (characters 32-126) plus a set of extra glyphs (dashes, accented
Latin punctuation, media icons, arrows, Greek capitals, and more) - see scripts/system-font-extra-chars.mjs for the
full list. No load step needed.
.(new (10, 10), , 'Score: 100');
.('Score: 100'); // → Vector2i (pixel width, height)- Use
BT.systemPrint()for demo-specific HUD panels and labels. Callpalette.applyHUD()at init so overlay and demo HUD share the same label/header/dim slot conventions - see API: Palette and Palette Presets - HUD. - The engine draws its own overlay (present FPS, target FPS, draw calls, frame/update()/render() timings, backend,
resolution, demo title) after each
render()whenisOverlayEnabledis true; see Overlay Guide. - For styled variable-width text, use a bitmap font instead.
API history
| Symbol | Since | Last changed | Status |
|---|---|---|---|
AssetLoader | 0.1.0 | – | stable |
BitmapFont | 0.1.0 | 1.7.0 | stable |
BT.loadingAssetsCount | 1.4.0 | – | stable |
IndexedSpriteLoadResult | 1.0.3 | – | stable |
SpriteSheet | 0.1.0 | – | stable |
1.7.0 - Fri Sep 11 2026
- Changed
BitmapFontAdded the `codePoints` getter, returning every Unicode code point the font defines a glyph for (ascending, derived live from the same glyph map `getGlyph()` and `hasGlyph()` read).
1.5.0 - Tue Aug 11 2026
- Changed
BitmapFont`getGlyph` / `getGlyphByCode` (and `measureText`, which now shares their lookup) substitute a font-defined fallback glyph for a missing character instead of returning `null`, when the font's glyph map has an entry keyed by `U+FFFD`.
1.4.0 - Thu Jul 23 2026
- Added
BT.loadingAssetsCount
1.0.3 - Sat May 16 2026
- Added
IndexedSpriteLoadResult
0.1.0 - Sat Dec 13 2025
- Added
AssetLoader - Added
BitmapFont - Added
SpriteSheet
See also
API: Core
API: Rendering
API: Palette
Palette Guide
Palette Presets
Bitmap Fonts
Hot Reload Guide
Overlay Guide
Testing
Last updated on October 18, 2026