Skip to main content
FastPack supports three packing algorithms, each optimized for different use cases. MaxRects produces the densest atlases, Grid creates uniform tile layouts, and Basic provides the fastest packing speed.

MaxRects (default)

MaxRects is the default packing algorithm. It produces dense atlases by tracking a list of maximal free rectangles and scoring candidate placements under a configurable heuristic.

How it works

The algorithm maintains a list of free rectangles covering all unoccupied atlas space. Initially the list contains one rectangle equal to the usable canvas (max dimensions minus border padding). For each sprite (processed largest-area-first):
  1. Score every free rectangle against the sprite dimensions using the active heuristic
  2. Place the sprite at the best-scoring position
  3. Split every free rectangle that overlaps the placed footprint (including shape padding) into up to four non-overlapping sub-rectangles
  4. Prune any free rectangle that is fully contained within another free rectangle
Sprites that cannot fit in any remaining free rectangle are returned as overflow. In multipack mode, overflow sprites seed the next atlas.

Heuristics

best-short-side-fit (default) — Minimizes the shorter leftover dimension after placement. Tends to produce compact, roughly square packing.
[algorithm]
type = "max_rects"
heuristic = "best_short_side_fit"

Configuration

[algorithm]
type = "max_rects"
heuristic = "best_short_side_fit"

[layout]
max_width = 4096
max_height = 4096
allow_rotation = true
shape_padding = 2
border_padding = 2
size_constraint = "any_size"  # any_size | pot | multiple_of_4 | word_aligned
force_square = false

Technical details

Shape padding is handled by adding the padding value to the sprite’s footprint width and height before scoring and splitting. The actual sprite rectangle is stored without padding; the gap appears as unused space between sprites. Border padding offsets the initial free rectangle inward so all placed sprites respect the configured atlas edge margin. Atlas size is computed after packing as the bounding box of all placed sprites plus border padding, then rounded up to satisfy size_constraint. If force_square is set, width and height are both raised to the larger of the two. Rotation is tested when allow_rotation is true and the sprite is not square. The rotated candidate is scored independently. The orientation with the better score wins. A rotated sprite is blitted 90° clockwise. Free-rect pruning is O(n²) per placement. For atlases with thousands of sprites this is the dominant cost.
MaxRects maps directly to TexturePacker’s MaxRects algorithm. All five heuristics have the same names (except TexturePacker uses “Best” as a prefix).

Grid

The grid packer divides the atlas into uniform cells of equal size and places each sprite into the next available cell in row-major order (left to right, then top to bottom). Grid packing is the right choice for animation frame strips and tile sets where all sprites are the same size. Its regular layout simplifies runtime frame calculation — a loader can compute any frame’s position from its index with a single multiply, without parsing a data file.

Configuration

Let FastPack compute cell size from the sprite set (uses the largest sprite’s dimensions):
[algorithm]
type = "grid"
cell_width = 0
cell_height = 0
There is no dedicated CLI flag for grid packing. Use a project file with [algorithm] type = "grid".

Example

Twenty 32×32 sprites packed into a 256-pixel-wide atlas with cell_width = 32, cell_height = 32:
┌────────────────────────────────────────┐
│  0   1   2   3   4   5   6   7        │  ← row 0 (8 cells × 32px = 256px)
│  8   9  10  11  12  13  14  15        │  ← row 1
│ 16  17  18  19                        │  ← row 2
└────────────────────────────────────────┘
All cells are 32×32. Sprites smaller than the cell are placed at the top-left of the cell; unused area is transparent.

Technical details

When cell_width or cell_height is 0, FastPack measures every sprite and uses the maximum width and height respectively.
Sprites larger than the explicit or derived cell size will overflow their cell and overlap the next — this is a configuration error.
The number of columns is floor(max_width / cell_width). The atlas height grows to fit all rows needed for the full sprite set. Despite the regular layout, FastPack still writes a full data file with exact frame rects. Sprites smaller than the cell have correct frame and sourceSize values, and pivot/nine-patch overrides work the same as with other algorithms.
TexturePacker calls this mode “Fixed Grid”. Cell-size derivation and row-major placement order match FastPack’s behavior.

Basic

The basic packer places sprites in horizontal strips. Starting from the top-left, it advances a cursor left-to-right across the row. When a sprite does not fit in the remaining row width, the cursor drops to the start of a new row below. Sprites within a row are top-aligned. Basic packing is fast but makes no attempt to minimize atlas area. Use it when pack speed is the priority, or as a quick sanity-check mode during development.

Configuration

[algorithm]
type = "basic"
--pack-mode fast selects the basic packer. good and best both use MaxRects.

Example

Five sprites of varying sizes packed with the basic algorithm:
┌──────────────────────────────┐
│ [64×96] [48×48] [32×32]  gap │  ← row 1
│                              │
│ [128×64] [16×16]         gap │  ← row 2
└──────────────────────────────┘
Row height equals the tallest sprite in that row. Shorter sprites leave vertical whitespace below them.

Technical details

Sprites are sorted by height descending before placement. This reduces wasted vertical space within rows by putting tall sprites together. The atlas width is the full max_width (default 4096); the basic packer does not search for a compact atlas width the way MaxRects does. The atlas height grows one row at a time until all sprites are placed.
Basic packing does not support multipack overflow. If sprites cannot fit within max_width × max_height, the pack fails with an error.
TexturePacker’s “Basic” algorithm uses the same row-strip approach. Atlas dimensions may differ because TexturePacker searches for an optimal width; FastPack’s basic packer always uses the full max_width.

Build docs developers (and LLMs) love