Build Photopea Premium
YESreplaces $5/mosaves $60/yrback to the verdict
An image editor in the browser where every hard problem is a resource problem: a layered document larger than the tab's memory, a filter that must not block the interface, and an undo history that cannot hold twenty full-resolution copies.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Builds the application and runs the test suite.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - have readyfree
Why Parsing is where work gets silently destroyed, and the specification is large enough that knowing what you do not support is most of doing this responsibly.
Get it Read the format documentation and collect real files including adjustment layers, effects and clipping masks.
Verify
Six real layered files open and their unsupported features are listed - have readyfree
Why Tiling, eviction and the memory ceiling are only meaningful against a document that genuinely does not fit, which is where naive implementations are killed by the browser.
Get it A high resolution layered document of a few hundred megapixels, or generate one.
Verify
Compositing it whole exceeds the memory ceiling - installfree
Why Filters and compositing cannot run on the interface thread, and what is available differs between browsers enough to affect the design.
Get it Check worker and offscreen canvas support in the browsers you intend to support before designing the pipeline.
Verify
A filter runs in a worker with the interface responsive - decidefree
Why The browser will kill the tab without warning, so the ceiling and the behaviour on approaching it are decisions rather than emergent properties.
Get it Decide the ceiling, and decide whether approaching it degrades quality, refuses an operation or prompts to save.
Verify
A written note states the ceiling and the behaviour on approach
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- documents(id, width, height, colour_mode, bit_depth, opened_from) - layers(id, document_id, ordinal, kind, name, opacity, blend_mode, visible, mask_ref, data_ref) - tiles(layer_id, x, y, level, data_ref, dirty) · the unit of rendering and eviction - operations(document_id, ordinal, kind, params_json, affected_region, at) · the undo history, as operations - unsupported(document_id, feature, layer_ordinal, detail) · what a file contained that this cannot represent - sessions(document_id, autosaved_at, storage_ref) · so a closed tab does not lose work - Undo stores operations with their affected region rather than snapshots, because a snapshot history for a large document exhausts memory long before the user runs out of edits.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
MEMORY_CEILING_MB | required | 1500 | The tab is killed without warning beyond this. Eviction targets it. |
TILE_SIZE | required | 256 | The unit of rendering and eviction. Smaller is finer and more overhead. |
AUTOSAVE_INTERVAL_SECONDS | required | 30 | Also on visibility change, since a discarded tab warns about nothing. |
UNDO_DEPTH | required | 50 | Achievable because history is operations rather than snapshots. |
OVERWRITE_AFTER_DROPPING | required | never | Present as a constant. Silently destroying layers is the worst outcome here. |
FILTER_PREVIEW_SCALE | required | 0.25 | Preview resolution. The applied result must still match its appearance. |
The build, in order
Opening a file and admitting what you cannot do
A layered file opens with its unsupported features listed rather than silently lost.
The structure is the point. A flattened import is an image viewer.
Files
src/parse.tsPer layer, where the user will see it.
Files
src/unsupported.tsSilently destroying work is the worst outcome in this build.
Files
src/save.tstests/no-lossy-overwrite.test.ts
done when · tick each as it passeswatch out- Flattening an adjustment layer or an effect you do not support, and then saving over the file, destroys work the person cannot recover, and they will not notice until much later. List what was not understood, and never overwrite the original when anything was dropped.
Rendering more document than fits
A document larger than available memory pans and zooms smoothly.
Whole-document compositing is what kills the tab.
Files
src/tiles.tsMemory bounded by viewport rather than document size.
Files
src/eviction.tsDownscaling full resolution per frame is the slow path.
Files
src/zoom.ts
done when · tick each as it passeswatch out- Compositing the whole document into one buffer works until the document is large, at which point the tab is killed by the browser and the work is gone with no error you can catch. Tile, render the viewport, and evict, so memory is bounded by the viewport rather than by the document.
Filters that do not freeze the tab
A slow filter runs with the interface still responding.
The interface thread must never do this work.
Files
src/filters/worker.tsAnd make sure the two look the same.
Files
src/filters/preview.tsVerified by comparison, not by intention.
Files
tests/cancel.test.ts
done when · tick each as it passeswatch out- Running a filter on the interface thread freezes the tab, and the browser eventually offers to close the unresponsive page, which is how a person loses an hour of work to a blur. Run in workers, and make cancellation leave the document exactly as it was.
Undo without holding twenty copies
Deep undo works on a large document within the memory ceiling.
Snapshots exhaust memory in a handful of edits.
Files
src/history.tsBalancing memory against recomputation time.
Files
src/checkpoints.tsBy comparison of the result, not by trusting the operations.
Files
tests/undo-redo.test.ts
done when · tick each as it passeswatch out- Storing a full snapshot per edit exhausts memory after a handful of operations on a large document, and the usual response is to reduce the history depth, which makes the editor frustrating. Store operations, checkpoint occasionally, and recompute, which trades a little time for a great deal of memory.
Not losing work when the tab closesproduct builder
A closed or crashed tab does not cost the session.
A discarded tab fires no warning.
Files
src/autosave.tsTested by killing the tab, not by closing it politely.
Files
tests/recovery.test.tsLosing layers on export is the second most common way work disappears.
Files
src/export.ts
done when · tick each as it passeswatch out- Relying on the browser's warning before closing means a crash, a tab discarded under memory pressure, or a mobile browser reclaiming the page loses everything, and none of those fire that warning. Autosave to local storage on an interval and on visibility change, and recover on reopening.
That is the whole plan for Photopea Premium. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Desktop editor feature parity.
- Professional colour management.
- Raw photographic processing.
- Collaborative editing.
- pixel-perfect professional tooling
- large template and asset libraries
- color-management edge cases
- cloud collaboration and mobile apps
- high-fidelity color, format, and export handling
- Non-destructive adjustment layers, which the operation-based history already suits
- Smart object equivalents, once unsupported-feature reporting is trustworthy
- A memory pressure indicator, since users currently discover the ceiling by losing a tab
Need the files? The project pack on the verdict page hands your agent the whole brief · more design.