Developing an online editor: where performance breaks
The editor easily handles a hundred objects, but add raster inserts, effects, and masks — and the response to dragging goes beyond half a second.
It's not about the network channel or server power: on every shift the document is rebuilt entirely — redrawing all layers, recalculating effects, updating panel thumbnails. The bottleneck of a graphic editor is not the operation itself, but the amount of work it drags along.
Load limits also show up in collaborative work on a layout. Five to ten people with independent undo histories produce divergent states: one has a shifted layer mask, another has a missing effect.
Until edits are merged into a single order, the editor shows each person their own version of the document, and the divergence is discovered only at export — when the layout has already gone to the client.
The third class of failures is losing edits when saving a layout. A full document rewrite is interrupted midway, and a half-state remains on the server that opens neither for the author nor for colleagues.
Unfinished sessions are also lost because the change history lives in the tab's memory, while only the last saved version resides on the storage side.
Behind all three failures is one architectural cause: the document is processed as a whole, not in parts, and has no reliable recovery point. We'll break down how this is addressed — at the level of the document model, version storage, and participant synchronization.
What changes after reworking the editor core
After reworking the core, the editor stops "stalling" on large layouts, and the user's work survives connection loss.
We measured the effect before and after across four values: interface response time, share of successful saves, recovery time after failure, and the class of failures that disappeared.
Interface response speed on large layouts
Previously, every action redrew the entire canvas: an edit in a layout with hundreds of layers caused a noticeable pause, dragging "caught up" with the cursor, and the layers panel froze.
The core was moved to incremental rendering — only the changed area updates, heavy operations went to a background thread, and the layer list is virtualized.
Response dropped from seconds to fractions of a second and almost does not depend on the number of layers: a layout several times heavier behaves the same.
User work preservation during connection loss
Previously, edits lived in the tab's memory: a network drop or save failure meant losing part of the layout. Now each operation is first recorded in the browser's local storage, and then goes to the server with retries and deduplication protection.
Recovery after failure takes seconds — the user returns exactly to the state in which the disconnection caught them.
Collaborative work on a layout
Edits are merged per object: changing one block does not block the entire layout, text is merged character by character, conflicts are resolved deterministically, and colleagues' cursors are visible in real time.
The share of successful saves stays above 99%, and the failure class "edits lost during disconnection" is completely gone.
Approaches to organizing the graphics core: comparison of approaches
The key fork is where the scene lives and where pixels are computed: on the server or in the user's browser. This determines both the editor's operating modes and how collaboration will behave as the number of participants grows.
Server-side and client-side rendering paths
Server-side layout rendering is predictable: the same file produces the same result regardless of device and system font set. Client-side rendering in the browser gives instant response to dragging and zoom because the frame is computed locally, without contacting the server.
This is how editors on the level of Canva are built, where the canvas and edit history live with the user.
| Option | When it fits | Limitations |
|---|---|---|
| Server-side layout rendering | Batch tasks: previews, print templates, reporting documents, fixed-format layouts | Editing lags: every step is a request and a queue. When the scene is saturated with objects, response time grows linearly |
| Client-side rendering in the browser | Interactive canvas where zoom and dragging without delay matter; large multi-layer layouts | Limited by tab memory: heavy scenes are split into layers and loaded in parts. Behavior depends on the browser |
| Hybrid: scene in the browser, heavy operations on the server | The product's main scenario: edits with the user, export and printing on the service side | Requires scene synchronization and a stable exchange format, otherwise versions diverge when edits conflict |
How to choose a mode for the load profile
We calculate the profile: how many objects are in a typical layout, how many participants edit it simultaneously, what share of sessions ends in export.
If the canvas is small and print accuracy is more important — we keep the scene on the server; if the layout grows to thousands of layers and is edited live — we compute in the browser and move export to the server.
The storage format is the same: a vector layer tree plus images as separate objects, without embedding into the scene file. This keeps the layout compact and collaborative edits merge quickly.
We lock in the editor architecture choice before development starts — on a prototype with the client's real layouts, where the maximum load is visible.
Implementation stages: from auditing the current code to handover
The stages of online editor development go from discovery to handover: at each step we record a verifiable result, not an abstract "readiness." We select the order of work based on the state of the current code — rewriting everything at once is almost never necessary.
What happens during the discovery stage
We begin the audit of the existing editor with the document model: layers, change history, file permissions, export and import formats. Separately, we look at where response time grows — canvas redraw, image and font loading, participant synchronization. The stage result is a bottleneck map and a work sequence with effort estimates.
- Discovery. Code, data, response time measurements on real scenarios.
- Data and permissions schema. Document format, versioning, roles, shared folders.
- Editor core. Canvas, layers, tools, export while preserving format compatibility.
- Collaboration. Edit history, autosave, rollback to a previous version.
- Editor load testing. Peak concurrent use and batch export of large layouts.
- Acceptance and handover. Verification against a scenario list, code, schemas, documentation.
How result acceptance works
Editor acceptance takes place on a test environment that replicates the production configuration: layout creation, co-authoring, export, recovery after connection loss. We check not only "it works" but also how the system behaves under load and after rollback.
Along with the code, we hand over the data schema, permissions description, and deployment procedure — so the team can develop the editor further without us.
Schemas, access policies, and release procedures
We do not hand over the editor as a "black box": along with documentation for the editor, by which a new engineer brings up the environment and understands the data model. Below is a list of artifacts that remain with the client's team after launch.
Storage and collaborative layout work schemas
- Layout storage schema: project and layer structure, object versions, relations to assets, cascade deletion rules.
- Permission matrix: roles from owner to viewer and what each can do with a layout, template, and brand book.
- Editor access policies: granting and revoking access, guest links, their lifetime, user activity log.
- Interface descriptions: contracts between the client side, rendering service, and storage when the format changes.
Release and rollback procedures
- Release procedure: build order, pre-deployment checks, composition of release owners.
- Rollback instruction: which version to roll back to, how to restore the previous build and not lose already created layouts.
- Migration procedure: how to change the layout format so that open works do not break for users.
We verify each artifact for completeness: the client's colleague reproduces release and rollback using the instruction without our support.
Case study: how the editor stopped losing edits under peak load
A template editor with collaboration: dozens of layers, text blocks, images, export. The first measurement showed that the bottleneck was not the interface, but state synchronization between participants and conflict resolution for edits in the layout. Below is what was reworked and how it was confirmed by load testing.
What was there before the rework
On layouts with two hundred or more layers, response to an action reached 2.5 seconds: every block move rebuilt the state of the entire document.
At peak — about three hundred concurrent sessions — the share of lost edits reached 7%, and session failures occurred in 4–6% of connections per hour.
Conflicts were resolved by the "last save wins" principle: two people moved the same block — one person's version silently disappeared.
What changed after
State was moved to per-layer operations: an edit is applied atomically to a specific object, not to the entire document. When one block is changed simultaneously, both versions are saved and shown to the author as options — the class of "silent" edit loss is gone. The smoothness benchmark was the level set by Canva, but it had to be maintained on heavy layouts.
Metrics after: response on a layout with two hundred or more layers — 250–300 ms instead of 2.5 s, lost edits — zero, session failures at peak — tenths of a percent. Batching operations reduced the number of server requests during peak minutes by about six times.
What to do if the editor is already launched and needs development?
A live editor is not a completed project, but a stream of changes: scenes, layers, action history, export.
Improving a ready editor begins not with code, but with taking measurements: where rendering falls apart with a large number of objects, on which operations response time grows, which errors users work around manually. Without these numbers, restructuring turns into an argument about tastes.
The editor's technical debt lies in predictable places: the document model is smeared across interface layers, canvas state is duplicated, export and saving are implemented separately and diverge in behavior.
Until this is separated, each new capability drags edits into five places and breaks neighboring scenarios.
Next is prioritizing bottlenecks by frequency of requests and cost of failure, not by loudness of requests. Usually rendering and change history are extracted first, then storage and export are decoupled: all scenarios gain speed at once, not just one.
Replacing the core without downtime goes through a parallel loop. The new version is brought up alongside, part of the traffic is directed to it, results are compared with the current ones — and only then are the rest switched over.
Switching is done with a flag on the service side, rollback — by reverting the flag; data migrates gradually, not in a single window.
Further development of the existing product proceeds in short releases with a common set of checks: the service remains operational at every step, and changes reach users in parts.
Technical doubts: performance, security, timelines
Engineering doubts usually concern not the editor itself, but what happens as load grows and when the project is handed over to another team. Below are answers to the questions that most often come up during estimation.
Several authors edit the same layout — what happens with conflicts?
Edits are made as separate operations on objects, not by rewriting the entire document, so two authors on neighboring layers do not block each other.
Changes reach other participants almost instantly, a conflict on a single object is resolved by a fixed rule, and every version of the layout remains in history — rollback to any point is available.
We have our own templates — will they have to be rebuilt from scratch?
No. The converter parses the source files: text and vector layers are transferred as editable objects, raster ones — as images with preserved proportions. The result is verified by batch comparison with the original, so the library migrates without manual rebuilding.
Will the editor withstand peak load?
Rendering and export are moved to separate services and scale independently of the interface, while heavy operations go into a queue and do not slow down other users. Responsiveness remains even as the number of concurrent sessions grows several times.
How are permissions separated in the editor?
Roles are configured at the account, folder, and individual layout level: view, comment, edit, publish. Permission checks are performed on the server on every request, not by hiding buttons in the interface, so the restriction cannot be bypassed through a request.
How are updates released if the editor is already in use?
We make data schema migrations backward compatible, deployment goes gradually and is accompanied by automatic rollback when errors grow. The author's active session is not interrupted: the unsaved layout is finalized in the background.
Let's discuss the task: we'll send an estimate and work plan
The first conversation takes 30–40 minutes and goes without presentations: we break down the user scenarios of the future editor, who works in it, what input and output formats are, how files are stored, and how access is arranged. This is enough to understand the class of the task, its scope, and where the main risks are.
From the client, a minimum is needed: examples of what the editor must be able to do, a list of import and export formats, requirements for collaboration and roles, integration points — login, storage, payment, analytics.
If some requirements are not yet defined, we record assumptions and verify them at the first stage, rather than building them into the estimate at random.
We come back with an estimate and a stage plan within 2–3 business days. The plan includes:
- work scenarios: canvas, layers, tools, undo, version history;
- separation of computations: what runs in the browser, what on the server, how export is arranged;
- data and storage schema: where files come from, where they are stored, how they are served to the user;
- list of integrations: login, payment, storage, analytics, external services;
- roles and access: who sees and edits what, limits on concurrent load;
- development stages with order, composition of each, and result acceptance points;
- effort estimate by stage and a list of risks with a mitigation plan.
Describe the task in free form: what kind of editor it is, who it is for, and what is already on hand. We'll come back with an estimate, a work plan, and a clear first step.







