Power-of-Two Textures and Atlas Sizing for Sprites
The first time a texture quietly doubled my game's memory footprint, I had no idea why. I'd exported a 1500x900 atlas, felt clever about packing it tight, and watched a mid-range Android phone choke on it. The culprit wasn't the file size on disk — it was that the GPU rounded my "tight" texture up to the next power of two behind my back, and I'd never learned the rules. This is the guide I wish someone had handed me then: what power-of-two means, why it still matters in 2026, and how to size frames and atlases so engines and phones stop fighting you.
What "power of two" actually means
A power-of-two number is one you get by doubling from 1: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096. When people say a texture is POT, they mean both its width and its height are numbers from that list — 256x256, 512x1024, 2048x2048, and so on. They don't have to be square, just powers of two on each axis.
That sounds like an arbitrary superstition until you remember the hardware grew up addressing memory in binary. A lot of the old optimizations — fast texture wrapping, clean mipmap chains, certain block-compressed formats — assume dimensions that divide cleanly by two over and over. Modern desktop GPUs stopped caring much (more on that below), but the assumption is still baked into mobile drivers, a chunk of compressed formats, and a surprising number of engine defaults. So it's worth getting right rather than guessing.
Why POT still matters (and when it really doesn't)
Here's the honest, non-academic split. POT matters when:
- You're shipping to mobile. Older and budget Android/iOS GPUs either require POT for some features or silently pad non-POT textures up to the next power of two — that's how my 1500x900 atlas became, effectively, 2048x1024 in memory.
- You use compressed texture formats. Formats like ETC2, PVRTC, and several others work in fixed blocks and frequently demand POT (PVRTC is famous for wanting square POT). If you want the memory savings of compression, you usually pay the POT tax to get there.
- You generate mipmaps. Mipmapping — the chain of half-size copies used so distant or scaled sprites don't shimmer — halves dimensions repeatedly. POT divides perfectly down to 1x1; a 1500-wide texture does not.
And POT genuinely doesn't matter much when you're a desktop-only or modern-console project rendering pixel-art sprites without mipmaps, using a recent GPU. Since the OpenGL ES 3.0 / WebGL 2 era, non-power-of-two textures are well supported with clamped sampling and no mips. If that's your whole audience, you can pack a 1300x740 atlas and move on with your life. The trap is assuming "works on my RTX desktop" means "works everywhere." It often doesn't, and you find out from a one-star review, not a crash log.
Atlas max sizes, and why bigger is not better
An atlas (or sprite sheet) is one big texture holding many frames so the GPU can draw them in fewer batches. Tempting to make it enormous and cram everything in — resist that. Two hard limits and one soft one:
- The device cap. Plenty of mobile GPUs cap maximum texture size at
4096. Hand them a 8192-wide page and it won't load, full stop.2048is the safe-everywhere number;4096is fine for most current hardware but still worth testing on real devices. - Memory. An uncompressed RGBA texture costs
width × height × 4bytes, period — it does not care how empty it is. So a2048×2048page is2048 × 2048 × 4 = 16,777,216bytes, about 16 MB in VRAM. A 4096x4096 page is 64 MB. Three of those and you've eaten 192 MB for art alone. - Load time. Bigger pages take longer to decode and upload, and a half-empty giant atlas wastes that time on transparent pixels.
The practical move is several right-sized pages rather than one monster. A few 1024 or 2048 atlases let the engine free the ones you don't currently need (the boss sheet doesn't have to live in memory during the tutorial), and they degrade gracefully on the weak phone in someone's pocket.
Padding and extrude: stopping texture bleed
This is the bug that looks like a haunting. You pack frames edge-to-edge, everything's pixel-perfect in the editor, and in-game a faint seam or a stray line of a neighbor's color flickers along a sprite's border — usually only when the camera moves or zooms. That's texture bleeding: when the GPU samples a texel with bilinear filtering or picks a mip level, it can reach one pixel past your frame's edge and grab whatever's next door.
Two fixes, often used together:
- Padding (a gutter): leave
1to2px of empty space between frames so the sampler has somewhere harmless to land. - Extrude: copy each frame's edge pixels outward into that gutter, so even if sampling overreaches, it grabs a duplicate of the correct edge color instead of transparency or a neighbor.
For crisp pixel art on point/nearest filtering you can sometimes get away with no padding, but the moment you scale sprites, rotate them, or enable mipmaps, add the gutter. It costs a few pixels of atlas space and saves an evening of "why is there a green line on my knight."
Frame sizes: pick a base grid
The cleanest way to make frames pack nicely into a POT page is to size the frames themselves on a sensible base grid — 16, 32, 64, or 128. Frames that are 32x32 or 64x64 tile into a 512 or 1024 page with no awkward remainder, no half-cells, no wasted strip down the side. They also stay crisp, because integer cell sizes line up with the pixel grid instead of landing on fractional coordinates that the renderer has to fudge.
Off-grid frame sizes like 50x37 are where the pain starts: they pack with gaps, the math for pivots and UVs gets ugly, and scaling produces soft edges. If your art is already that size, you don't have to redraw it — you normalize it to a consistent cell first.
Where loopsprite fits
This is the unglamorous prep work the Sprite Slicer exists for. If your frames are inconsistent sizes — a tall jump pose here, a wide attack there — the normalize feature pads every frame to a single uniform cell so they pack into a grid cleanly, with consistent spacing the engine can slice with one rule. That's the difference between an atlas that drops into Unity or Godot in thirty seconds and one you hand-tweak rect by rect.
The typical flow: drop in your sheet or loose frames, normalize them to a common cell sized on a base grid, then either slice the sheet into individual frames or keep it as one page. It all runs in the browser, nothing uploads anywhere, and you control the cell size, the gutter, and the grid. Once it's normalized and padded, importing is boring — which, for once, is exactly what you want when you bring the sheet into Unity.
A quick checklist
- Decide your target. Mobile or compressed or mipmapped → go POT. Modern desktop, pixel art, no mips → POT is optional.
- Cap atlas pages at
2048for safety,4096only after testing real devices. - Do the memory math:
width × height × 4bytes uncompressed. Right-size, don't max-size. - Size frames on a 16/32/64/128 grid so they tile into the page.
- Add a
1–2px gutter, and extrude edges if you scale, rotate, or mip. - Normalize inconsistent frames before packing so the grid is uniform.
FAQ
Q. Does my whole atlas have to be a power of two, or just the frames?
The page (the atlas texture) is the one the GPU sizes, so that's the dimension that should be POT when POT matters. Individual frames don't need to be powers of two — they just need to tile into the page neatly, which is why a base grid like 32 or 64 helps. Frames can be 64x64 inside a 1024x1024 POT page perfectly happily.
Q. I'm desktop-only and never use mipmaps. Can I just ignore POT?
Largely, yes. Modern desktop GPUs handle non-power-of-two textures fine for sprite rendering with point or bilinear filtering and no mips. The risk is purely future-you porting to mobile or switching on compression later and rediscovering the rule the hard way. If there's any chance of a mobile build, sizing POT now is cheap insurance.
Q. How much padding between frames is enough?
One pixel handles most bilinear sampling; two pixels is the comfortable default once mipmaps are involved, because lower mip levels effectively sample from farther away. Pair the gutter with edge-extrude for the best result — extrude defends against the overreach, the gutter keeps neighbors from being what's overreached into.
Q. Why did my 1920x1080 atlas use way more memory than the PNG suggested?
PNG is compressed on disk; in VRAM the texture is uncompressed unless you used a GPU-compressed format. A non-POT 1920x1080 page can also get padded up to 2048x2048 on hardware that requires POT, and at that point you're paying 2048 × 2048 × 4 = 16 MB regardless of how much of it is transparent. Right-size the page and you stop paying for empty space.
Q. My frames are all different sizes. Do I have to redraw them to a grid?
No. Use the Sprite Slicer's normalize feature to pad every frame into one uniform cell sized on a base grid. The art stays exactly as drawn, just centered in a consistent box, which is what lets it pack cleanly and slice with a single grid rule.
Q. Should I ship one big atlas or several smaller ones?
Several smaller, almost always. Multiple 1024 or 2048 pages let the engine load and unload art by context, stay under device texture caps, and load faster than one half-empty giant. One mega-atlas only wins if literally everything is on screen at once, which is rare. See the sheet vs individual frames guide for the batching tradeoffs.