Upload appears to hang: image compression blocks the main thread #5

Closed
opened 2026-09-11 23:15:48 +00:00 by iec-admin · 0 comments
Contributor

Follow-up to #3.

Symptom

After picking a large image the admin appears to "just keep loading" — the overlay sits on Preparing…, the progress bar stays at 0%, and the page is unresponsive.

Cause

Two defects in the compression added in #3:

  1. Too many encodes. The quality ladder was walked at every scale — up to 25 full re-encodes of a large canvas. Measured on a 12 MP incompressible image: 4,721 ms across 11 encodes on a fast laptop. On a mid-range machine that is tens of seconds.
  2. Nothing yielded to the event loop. canvas.toBlob on a 12 MP canvas blocks the main thread, so React never repainted and the bar was frozen at 0% — which reads as a hang rather than as work in progress.

Fix

Estimate the next scale from how far the previous attempt overshot the budget (encoded size tracks pixel count closely at fixed quality) instead of walking a fixed ladder, capped at 4 attempts. Yield between attempts so the UI can paint. Show an indeterminate pulsing bar while preparing, since re-encoding reports no progress of its own.

Measured

Case Before After
12 MP incompressible (worst case) 4,721 ms / 11 encodes 1,367 ms / 2 encodes
2.4 MB realistic photo 359 ms / 1 encode 418 ms / 1 encode

Output quality is unaffected: the realistic photo still lands at full 2560×1920.

Follow-up to #3. ## Symptom After picking a large image the admin appears to "just keep loading" — the overlay sits on `Preparing…`, the progress bar stays at 0%, and the page is unresponsive. ## Cause Two defects in the compression added in #3: 1. **Too many encodes.** The quality ladder was walked at every scale — up to 25 full re-encodes of a large canvas. Measured on a 12 MP incompressible image: **4,721 ms across 11 encodes** on a fast laptop. On a mid-range machine that is tens of seconds. 2. **Nothing yielded to the event loop.** `canvas.toBlob` on a 12 MP canvas blocks the main thread, so React never repainted and the bar was frozen at 0% — which reads as a hang rather than as work in progress. ## Fix Estimate the next scale from how far the previous attempt overshot the budget (encoded size tracks pixel count closely at fixed quality) instead of walking a fixed ladder, capped at 4 attempts. Yield between attempts so the UI can paint. Show an indeterminate pulsing bar while preparing, since re-encoding reports no progress of its own. ## Measured | Case | Before | After | |---|---|---| | 12 MP incompressible (worst case) | 4,721 ms / 11 encodes | **1,367 ms / 2 encodes** | | 2.4 MB realistic photo | 359 ms / 1 encode | 418 ms / 1 encode | Output quality is unaffected: the realistic photo still lands at full 2560×1920.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
martin/ballantyne-www#5
No description provided.