Skip to content

Architecture

edgesharp architecture diagram

The image processing engine is written in Zig, compiled to WebAssembly with Relaxed SIMD enabled.

Binary size: 172 KB raw / ~87 KB gzip (Zig WASM, includes libwebp + miniz). The full Worker bundle including libavif lands at ~838 KB gzip.

ModuleLanguagePurpose
jpeg.zigPure ZigBaseline JPEG decoder (Huffman, IDCT, YCbCr→RGB)
decode.zigPure ZigPNG decoder (all color types, zlib inflate)
encode.zigZig + CJPEG encoder (DCT) + PNG encoder (miniz) + WebP encoder (libwebp)
webp_encode.zigZig→CBinding to libwebp for WebP encoding
resize.zigPure ZigLanczos3 with SIMD FMA + premultiplied alpha
deflate.zigZig→CBinding to miniz for zlib compression
memory.zigPure ZigWASM heap allocator
libc_glue.zigPure Zigmalloc/free/memcpy/math functions for C libraries

Three C libraries are statically linked into the single Worker:

  • miniz (public domain), deflate compression for PNG encoding. Smaller and more predictable than Zig stdlib’s flate path on freestanding WASM.
  • libwebp (BSD), WebP encoding. Patched cpu.c to add __wasm__ detection (upstream only checks for EMSCRIPTEN). Software math functions in libc_glue.zig replace Zig builtins that recurse on freestanding WASM.
  • libavif + libaom: AVIF encoder, always bundled. Custom-built (see tools/build-avif/) with size-first emcc flags and 8-bit-only AV1, weighing ~1.5 MB versus jsquash’s stock ~3.4 MB build. Lazily instantiated on the first AVIF request, so Workers that never serve AVIF never pay its startup cost; set DISABLED_FORMATS="avif" in the Cloudflare dashboard for a runtime kill switch.

The build targets wasm32-freestanding with these features:

  • simd128: 128-bit SIMD vectors
  • relaxed_simd: fused multiply-add (FMA) for Lanczos kernel
  • bulk_memory: fast memory operations
  • sign_ext: sign extension instructions

The Lanczos3 resize uses @mulAdd(Vec4, a, b, c) which compiles to f32x4.relaxed_madd: processing 4 RGBA channels per instruction.

edgesharp premultiplies alpha before resizing and unpremultiplies after, matching Sharp/libvips behavior. Without this, transparent edges get color fringing because the Lanczos kernel blends RGB values from fully transparent pixels.

16 deterministically named DOs (img-slot-0 through img-slot-15) hold warm WASM instances. The slot is chosen by hashing the image URL.

V8’s compilation tiers:

  1. Liftoff (first request), fast compile, slower execution
  2. TurboFan (subsequent requests), optimized SIMD code, full speed

Because the DOs persist between requests, TurboFan has time to optimize the WASM. Cold starts (~2-3s for WASM compilation) only happen when a DO is evicted.

URLs encode all transform parameters (url, w, q, format), so each unique combination is a distinct cache key. Responses are immutable: the URL uniquely identifies the output.

Cache hierarchy:

  1. Cache API: per-datacenter, free, evicted under memory pressure
  2. R2: persistent, survives cache eviction. $0.015/GB-month storage, free egress, so cached output ships back to viewers at no bandwidth cost
  3. WASM: recomputes on double-miss (extremely rare)

The Vary: Accept header ensures the CDN caches different format responses separately (JPEG vs PNG vs AVIF).