A 60-frame animation can contain perfectly valid PNGs and still fail when a browser combines them into one sprite sheet. The failure is often not the compressed file size. It is the decoded canvas size, the source frames held in memory, and the encoder's working allocation.
Start with the final pixel budget
For a fixed-cell sheet without margins or gutters:
- sheet width = columns × cell width
- sheet height = rows × cell height
- pixel count = sheet width × sheet height
- one RGBA buffer ≈ pixel count × 4 bytes
With 60 frames at 960×960 arranged 6×10:
| Item | Value |
|---|---|
| Sheet size | 5760×9600 |
| Pixels | 55,296,000 |
| One RGBA buffer | about 210.94 MiB |
That estimate covers only one uncompressed RGBA buffer. A real export can also retain decoded source images, intermediate canvases, encoder memory and the final Blob. A 10 MB PNG on disk therefore does not imply a 10 MB browser operation.
Preserve resolution by changing delivery layout
The resilient fallback is a numbered PNG sequence:
run_001.png
run_002.png
...
run_060.png
Add a small manifest with frame count and intended playback FPS. If the engine requires atlases, pack smaller pages per action or per texture budget later. The source sequence remains editable and complete.
This is better than shrinking every source frame simply to make one giant sheet encode.
Validate before allocating a canvas
Export code should check width, height and total pixel area before creating the canvas. If the request is too large, offer sequence export immediately instead of attempting an allocation that may fail or freeze the page.
When using HTMLCanvasElement.toBlob, also handle a null result. Preserve the user's frames and fall back to the sequence rather than producing an empty download.
Capacity and CORS are different failures
A large allocation is a capacity problem. A tainted canvas is an origin-security problem caused by drawing cross-origin pixels without the required CORS permission. Downscaling does not repair missing CORS headers, and changing CORS settings does not make a 55-million-pixel sheet cheap.
Report these conditions separately so users get the right recovery path.
Practical export checklist
- Calculate final width, height and pixel area.
- Keep full-resolution numbered frames as the production master.
- Split sheets by action or engine texture-page budget.
- Handle a null
toBlobresult. - Report CORS failures separately from capacity failures.
- Verify ZIP count plus first, middle and last frame.
I work on FrameSprite, where this calculation came from a real 60-frame browser-export case. The complete 12/24/60-frame comparison and failure table are here:
Top comments (0)