Skip to main content

Image Proxy Loading

High-resolution photos are the fastest way to crash a mobile browser. Canvas memory is driven by decoded pixels (width × height × 4 bytes), not file size — a single 6000×4000 photo costs ~96 MB of RAM decoded, regardless of how well the JPEG compresses on disk. A few of those on one canvas will exhaust iOS Safari's canvas budget and kill the tab.

Image proxy loading solves this by rendering a downscaled proxy on the canvas while keeping the full-resolution original for print export. Editing stays smooth and light; orders and exports are always full quality. source never changes — it always references the original.

There are two modes, and they compose:

ModeWhere the proxy comes fromSaves
Client-side proxying (default)Generated in the browser after the original downloadsMemory
Hybrid (server-side) proxyingA separate server-hosted variant URL (proxySource); the original is never fetched client-sideMemory and bandwidth

Both leave source untouched, so exports and orders are identical either way.

Client-side proxying (automatic)

By default, any raster image whose natural width or height exceeds the imageProxyMaxDimension option (default 1600) is downscaled to an in-memory proxy for on-canvas editing. A 25% slack applies — images only slightly over the cap aren't worth downscaling — so the effective trigger is 1600 × 1.25 = 2000px.

The proxy is a <canvas> element generated from the loaded original. It is never re-encoded to JPEG/PNG and never serialized, so there is no format, transparency, or payload concern.

What is preserved:

  • source, logical dimensions, and scaleX/scaleY are unchanged — serialization and orders always reference the full-resolution original.
  • Print exports — both server-side and exportPrintAreaToSVG() — render from the original, never the proxy.
  • DPI warnings and the size tooltip rate the original via originalWidth/originalHeight, which are serialized in the element JSON for proxied images (alongside originalFileSize when known).
  • isProxy is exposed at runtime on the element so your UI can react to it. It is not serialized.

Notes:

  • SVGs are never proxied — they're vector, so there's no decoded-pixel cost to avoid.
  • Generation prefers createImageBitmap with resize options (downscales during decode, avoiding a full-resolution intermediate canvas) and falls back to stepped canvas halving where that isn't supported.
  • Proxies are cached in-memory (bounded LRU) keyed by source + max dimension, so the same image reused across multiple views generates one proxy.
  • If generation fails for any reason, the canvas renders the full-resolution original — proxying never blocks an image from appearing.

Configuration

imageProxyMaxDimension is a global option:

const chamevo = await createCustomizer('#el', {
imageProxyMaxDimension: 1600, // default
});
ValueEffect
1600 (default)Downscale raster images larger than ~2000px (with slack).
Higher (e.g. 2400)Sharper on-screen zoom, more memory per image.
0Disable proxying entirely — always render originals.

See the options reference for the full option list.

Opting out per element or per group

Set imageProxy: false on an element to always render its original, regardless of imageProxyMaxDimension:

await chamevo.canvas.addElement('image', largePosterUrl, 'Poster', {
imageProxy: false, // always render full resolution
});

Because imageProxy flows through the parameter cascade, you can exempt whole groups of images:

const options = {
// Configured product images always render at full resolution…
imageParameters: { imageProxy: false },
// …while user uploads stay proxied (the default).
};

Use customImageParameters: { imageProxy: false } for the reverse — proxy configured images but never user uploads.

Hybrid (server-side) proxying

When an element carries a proxySource URL plus originalWidth/originalHeight, the canvas loads only that server-hosted variant. The full-resolution source is never downloaded client-side, so you save the download bandwidth on top of the decode memory — a significant win on mobile connections and for very large originals.

await chamevo.canvas.addElement('image', originalUrl, 'Photo', {
proxySource: 'https://cdn.example.com/uploads/photo-1600.jpg',
originalWidth: 5760,
originalHeight: 3840,
});

Behavior:

  • The canvas renders the variant at proxySource; source stays the full-resolution original for orders and export.
  • originalWidth/originalHeight are required — DPI warnings and the size tooltip rate the original, and they must be known without downloading it.
  • proxySource is serialized. Restored designs keep skipping the original, so reopening a saved design is just as light as the first load.
  • Fallback: if the variant fails to load, the original loads with client-side proxying applied — the image always appears.

Automatic wiring

You rarely set proxySource by hand. The built-in modules wire it for you:

  • Uploads module (cv-image-source-upload) — when the upload server's JSON response includes an image_proxy_src, the module attaches it as proxySource along with the returned width/height as originalWidth/originalHeight.
  • Pixabay picks (cv-image-source-pixabay) — high-resolution hits use Pixabay's own webformatURL as the variant, so the canvas loads the web-sized image while source stays the full imageURL.

Upload server contract

To enable hybrid proxying for uploads, your upload endpoint returns the variant URL alongside the original. The response fields the uploads module reads:

{
"source": "https://cdn.example.com/uploads/photo.jpg",
"image_proxy_src": "https://cdn.example.com/uploads/photo-1600.jpg",
"width": 5760,
"height": 3840
}
  • image_proxy_src → element proxySource (omit or null to fall back to client-side proxying)
  • width / heightoriginalWidth / originalHeight

The playground ships a reference implementation that generates the downscaled variant server-side and returns these fields — see apps/playground/php/file-upload/upload-image.php for the full flow.

Element fields reference

Set on ElementParameters (see the elements guide):

FieldTypeDefaultDescription
imageProxybooleantrueAllow proxy loading for this element. false = always render the full-resolution original.
proxySourcestringServer-hosted downscaled variant URL (hybrid mode). Requires originalWidth/originalHeight. Serialized.
originalWidthnumberNatural pixel width of the original. Serialized for proxied images; required for proxySource.
originalHeightnumberNatural pixel height of the original. Serialized for proxied images; required for proxySource.
originalFileSizenumberOriginal file size in bytes, when known. Serialized.
isProxybooleanRuntime only (not serialized). true when the canvas is currently rendering a proxy.

Global option (see the options reference):

OptionTypeDefaultScopeDescription
imageProxyMaxDimensionnumber1600globalMax on-canvas pixel dimension for raster images. Larger images are proxied. 0 disables.

Guarantees

Whichever mode is active, the contract is the same: source always stays the full-resolution original, and orders plus print exports are byte-for-byte identical to a non-proxied design. Proxying is a rendering optimization — it never changes what the customer buys or what ships to production.