Memory
A render keeps several copies of the image in memory at once. estimateMemoryMB(width, height) returns the peak for an original of that size: 24 bytes per pixel plus 256 MB for the runtime.
import { estimateMemoryMB } from '@sealpixel/render';
estimateMemoryMB(8000, 6000); // 1408| Original | Megapixels | Estimate | Lambda size to start from |
|---|---|---|---|
| 4000 × 3000 | 12 | 544 MB | 1024 MB |
| 6000 × 4000 | 24 | 832 MB | 1769 MB (one full vCPU) |
| 8660 × 5774 | 50 | 1457 MB | 2048 to 3008 MB |
The core runs on one thread. On Lambda, the 1769 MB tier gives one full vCPU, so memory also sets the speed.
limits.maxMegapixels is the size you have to plan for. The renderer refuses a larger original from its header, before it decodes a pixel. The TOO_LARGE message names the memory that the original would need.
Concurrent renders
One render runs at a time per process. Concurrent calls of render() run their checks at once, but the decode, the render and the encode run one after another. A render that waits holds only the encoded bytes of its original and its assets, not a decoded image. The peak is therefore about one render plus the encoded input bytes of the waiting requests. More concurrent requests make the process slower per request, not larger.
Where the bytes go
Measured on Node 24 with a JPEG original, per pixel of the original:
| Where | Bytes per pixel |
|---|---|
| WebAssembly memory, heaviest stage (annotate, sticker, frame, watermark: the input, a premultiplied copy and the output) | 12 |
| WebAssembly memory, crop | 9 |
| WebAssembly memory, color, redact, resize | 4 to 5 |
| The decoded image and the rendered image as JS arrays | 8 |
| One render, process peak above the idle runtime | 19 to 24 |
The renderer decodes the original once and passes the decoded image to the render. WebAssembly memory grows to its peak and does not shrink, so it stays allocated for the life of the process.
result.stream() does not lower the peak. The encoder builds the whole file in memory, and the stream starts after that.
The ceiling
The core accepts at most 2^28 pixels (268 megapixels, 16384 × 16384) and 65535 pixels per side, for the original and for the output of every stage. A plan whose output is larger fails with TOO_LARGE before the core allocates the buffer.