Building sprite sheets for game engines is a deceptively messy task. The source frames are correct, but getting them into a correctly ordered, correctly sized, power-of-two-compliant texture atlas without accidentally distorting artwork or wasting GPU memory requires decisions that most general-purpose image tools don't support.
The Sprite Sheet Assembly Tool is a free, open-source desktop utility that handles the full pipeline from ordered frame sequences to a game-ready PNG — with explicit control over grid layout, canvas mode, and POT memory implications at every step.
The tool is designed around one constraint that game engines impose and image editors ignore: output order is always left-to-right, top-to-bottom, and the layout must be predictable by the shader sampling the texture at runtime. Any reordering must happen before export, not after.
| FEATURE | DETAIL |
|---|---|
| Frame input | Add whole folder or individual files. Supports PNG, JPG, JPEG. |
| Frame inspection | Scrollable list with name, dimensions, and thumbnail per frame. |
| Empty frame detection | Fully transparent source images detected and labelled as intentional EMPTY FRAME — not silently skipped. |
| Reordering | Drag-and-drop, move controls, or natural filename sort. |
| Animation preview | Plays back the ordered sequence with adjustable delay in milliseconds. |
| Grid recommendations | Nearest-square, POT-optimised, and custom column count — with frame count, cell size, empty slots, and memory utilisation per option. |
| Canvas modes | Exact Grid (safe default) or POT Rescale with live distortion warnings. |
| Export | Transparent PNG with standardised filename encoding all layout metadata. |
Keeps raw grid resolution. No rescaling — pixel dimensions are exactly columns × cell_width by rows × cell_height.
Correct default for modern engines (Unity, Unreal, Godot) that support non-POT textures natively. No distortion risk. No GPU memory inflation from padding.
Use when: engine supports NPOT textures and you want pixel-perfect output.
Rescales output to nearest Power-of-Two dimensions using nearest-neighbour sampling. For an N×N grid, uses width-derived POT side (e.g. 5×5 targeting width 2048 → 2048×2048). Rectangular grids use independent POT width and height.
The UI shows separate X/Y scale percentages and a distortion warning when X≠Y scale. Required for older mobile hardware and specific engine texture import settings.
Use when: target platform or engine requires strict POT textures.
POT Rescale changes artwork dimensions. Nearest-neighbour is used to preserve hard pixel edges, but any asymmetric scale (X≠Y) distorts the frames. The tool displays explicit X and Y scale percentages and a distortion warning before export — it never silently applies a potentially destructive operation.
The tool computes multiple grid layouts and ranks them by POT canvas area utilisation:
Higher utilisation means less transparent GPU texture memory — the frames cover more of the allocated texture space. The nearest-square preset uses ceil(sqrt(frame count)) for both dimensions. For 35 frames this gives a 6×6 grid — 36 slots, 1 transparent placeholder — which is not the same as a 1024×1024 POT texture. The UI makes this distinction explicit: grid dimensions describe cells, canvas dimensions describe pixels.
| FRAME COUNT | NEAREST SQUARE | SLOTS | EMPTY SLOTS |
|---|---|---|---|
| 16 | 4×4 | 16 | 0 — perfect fit |
| 24 | 5×5 | 25 | 1 |
| 35 | 6×6 | 36 | 1 |
| 48 | 7×7 | 49 | 1 |
| 60 | 8×8 | 64 | 4 |
Empty slots are exported as transparent cells — they remain part of the grid and are highlighted in the UI. Selecting a preset preserves the reserved rows during export. Manually changing the column count returns to automatic row calculation.
The export filename encodes all layout metadata needed to reconstruct the sprite sheet parameters without opening the file:
// Naming format:
sprite_sheet_<frame-count>f_<columns>x<rows>_<output-width>x<output-height>_<mode>.png
// Examples:
sprite_sheet_024f_6x4_1024x512_exactgrid.png
sprite_sheet_035f_6x6_2048x2048_potfit.png
sprite_sheet_016f_4x4_512x512_exactgrid.png
| SEGMENT | ENCODES | EXAMPLE |
|---|---|---|
| 024f | Total frame count — what the shader expects to index | 24 frames in sequence |
| 6x4 | Grid layout — columns × rows used for UV calculation | 6 columns, 4 rows |
| 1024x512 | Output pixel dimensions — matches the actual file | 1024 wide, 512 tall |
| exactgrid / potfit | Canvas mode — tells you whether rescaling was applied | No distortion / POT rescaled |
This convention makes the asset self-documenting. A shader sampling the sprite sheet at runtime needs exactly the frame count and grid dimensions to calculate UV offsets — both are visible in the filename without requiring a sidecar metadata file or database lookup.
The tool is structured as a Python desktop application with a clean separation between data logic and UI. The entry point is always app.py — no other file is a supported entry point.
| FILE / PATH | ROLE |
|---|---|
| app.py | Application conductor — only supported entry point |
| src/bridge.py | UI-to-service coordination layer — decouples UI events from business logic |
| src/frame_service.py | Image discovery, validation, and thumbnail generation |
| src/grid_optimizer.py | Grid layout calculations and Power-of-Two recommendations |
| src/sheet_builder.py | Ordered sprite sheet rendering — composites frames into output PNG |
| ui/ | HTML, CSS, and interaction logic for the desktop UI |
| install.py | Cross-platform dependency verification — safe, isolated, audit-first |
| tests/ | Core algorithm and export tests |
The grid optimizer and sheet builder are the core algorithmic modules. Frames of different sizes are supported — the largest width and height across all frames defines the shared cell size, and smaller frames are centred within their cells. This means mixed-resolution sequences export correctly without manual padding.
// Step 1: install once
win_install.bat
// Step 2: run anytime
win_RUN.bat
// Debug mode (keeps console open)
win_RUN_debug.bat
// Verify dependencies — read-only, no changes
.venv\Scripts\python.exe install.py --check
// Step 1: make scripts executable
chmod +x lnx_install.sh lnx_RUN.sh
// Step 2: install once
./lnx_install.sh
// Step 3: run anytime
./lnx_RUN.sh
// Verify — read-only
.venv/bin/python install.py --check
| GUARANTEE | DETAIL |
|---|---|
| Isolated environment | All dependencies install into a project-local .venv — never touches user or system Python |
| Audit before install | Missing packages listed and confirmed before any pip install runs |
| Safe default | Pressing Enter or answering anything except Y/Yes cancels safely |
| No silent upgrades | Installer never upgrades pip or modifies packages outside .venv |
| Refuses broken envs | Existing incomplete, linked, or system-site-enabled .venv directories refused — not overwritten |
| Read-only check | install.py --check never prompts or installs — safe to run anytime for diagnostics |
| Linux runtime support | Supports apt, dnf, and pacman for missing Python runtime — asks before every system operation |
install.py --yes is available for deliberate automation pipelines, but still refuses to run outside the project .venv. The flag bypasses the confirmation prompt — it does not bypass the isolation requirement.
| STEP | ACTION | NOTES |
|---|---|---|
| 1 | Add Files or Add Folder | PNG, JPG, JPEG supported. Folder adds all matching files in the directory. |
| 2 | Arrange frame order | Drag-and-drop, move controls, or natural filename sort. First row = top-left cell in output. |
| 3 | Preview animation | Press Play, set frame delay in ms. Verify sequence before committing to export. |
| 4 | Select grid layout | Apply a recommendation or enter custom column count. Review frame count, cell size, empty slots, and POT utilisation. |
| 5 | Choose canvas mode | Exact Grid (no rescale) or POT Rescale. Review RAW → OUTPUT resolution and distortion warning. |
| 6 | Save Sprite Sheet | Choose destination path. Filename is auto-generated with full layout metadata encoded. |
Frames of different sizes are fully supported. The tool calculates the largest width and height across all frames and uses those as the shared cell dimensions — smaller frames are centred. You do not need to pre-pad frames to a uniform size before import.