Appearance
To enable deep zoom with IIIF you need three things in alignment: a pyramidal source image (pyramidal TIFF or JPEG 2000), a conformant IIIF Image API server that can crop arbitrary regions, and a deep-zoom client such as OpenSeadragon or Mirador reading the server's info.json. Get those three right and zoom is automatic — the client requests only the tiles visible at the current zoom level, so a 400-megapixel map loads as fast as a thumbnail.
What actually makes zoom "deep"?
Deep zoom is not a feature you switch on; it is the consequence of serving an image as a pyramid of pre-computed resolution levels. The client asks for a small region at a coarse level when zoomed out, then progressively finer regions as the user zooms in. The IIIF Image API expresses this through the URL grammar:
{scheme}://{server}/{prefix}/{identifier}/{region}/{size}/{rotation}/{quality}.{format}A tile request for the top-left 512×512 square at full resolution looks like:
https://images.example.org/iiif/3/map_0042/0,0,512,512/512,/0/default.jpgThe viewer composes hundreds of these requests as you pan. If each one forces the server to decode a 1.2 GB flat TIFF, performance dies. A pyramidal master lets the server jump straight to the right level.
How do I prepare a pyramidal source?
Convert your archival master to a tiled, multi-resolution format. For pyramidal TIFF with libvips:
bash
vips tiffsave master.tif map_0042.tif \
--tile --pyramid --compression jpeg --Q 90 \
--tile-width 256 --tile-height 256For JPEG 2000 with Kakadu's kdu_compress, request multiple resolution levels and tiling:
bash
kdu_compress -i master.tif -o map_0042.jp2 \
Clevels=6 Stiles="{1024,1024}" -rate 2.0Six levels handles roughly a 64× zoom range, which covers most manuscript and map use cases.
Which tile size and scaleFactors should info.json advertise?
The server publishes its capabilities in info.json. A practitioner-friendly block looks like this:
json
{
"@context": "http://iiif.io/api/image/3/context.json",
"id": "https://images.example.org/iiif/3/map_0042",
"type": "ImageService3",
"protocol": "http://iiif.io/api/image",
"width": 24000,
"height": 18000,
"tiles": [
{ "width": 512, "height": 512, "scaleFactors": [1, 2, 4, 8, 16, 32] }
]
}Advertise scaleFactors that match the levels actually present in your source. Mismatched factors are the single most common cause of "tiles load at one zoom but not another".
What are the trade-offs between tile sizes?
| Tile size | Requests per view | Decode cost | Best for |
|---|---|---|---|
| 256 | More (smaller squares) | Low per tile | Mobile, weak networks |
| 512 | Balanced | Moderate | General default |
| 1024 | Fewer | High per tile | CDN-fronted, huge masters |
Larger tiles mean fewer HTTP round-trips but more wasted pixels at the edges of the viewport. 512 is almost always the right starting point.
Why does my deep zoom look blurry at maximum magnification?
Because the viewer is upscaling beyond the master's true resolution. If info.json reports width: 6000 but your users expect to read individual ink strokes, the scan itself is too low. Deep zoom reveals captured detail; it cannot fabricate it. Re-digitise at 400–600 ppi for manuscripts you expect heavy magnification on.
A working checklist before you call it done
- [ ] Master is pyramidal (verify with
vips tiffinfoorkdu_v_expand). - [ ]
info.jsonwidth/height match the real pixel dimensions. - [ ] scaleFactors correspond to actual pyramid levels.
- [ ] Tile size is consistent across the collection (pick 512).
- [ ] OpenSeadragon loads the service with no console 404s.
- [ ] A non-JS fallback link to a downsized full image exists.
- [ ] CDN caches tile responses with a long
max-age.
Key Takeaways
- Deep zoom is the product of a pyramidal source plus a conformant Image API server plus a tiled client — all three must agree.
- Generate pyramids with
libvips(pyramidal TIFF) or Kakadu (JP2); 6 levels covers most needs. - Advertise honest width, height, tile size and scaleFactors in
info.json. - 512 px tiles are the sensible default; go smaller for mobile, larger behind a CDN.
- Blur at maximum zoom is a digitisation-resolution problem, not a IIIF problem.
- Always provide a non-JavaScript fallback image for accessibility and crawlers.
Frequently Asked Questions
Do I need pre-generated tiles for IIIF deep zoom?
No. A conformant IIIF Image API server such as Cantaloupe or IIPImage derives tiles on the fly from a pyramidal source. Pre-tiling (static tiles) is only needed when you have no image server and serve flat files from object storage.
What tile size should I advertise in info.json?
512 is the safe default and matches OpenSeadragon and Mirador expectations. Use 256 only for very low-bandwidth audiences, and 1024 if your CDN caches aggressively and your images are huge.
Why does my image only zoom to a blurry level?
Your source is not pyramidal or its native resolution is lower than you think. Deep zoom can never invent detail above the master's true pixel dimensions, so check the width and height in info.json against your scan.
What is the difference between scaleFactors and tile size?
Tile size is the pixel dimension of each requested square; scaleFactors are the pyramid levels (1, 2, 4, 8...) at which the server can deliver tiles without resampling the full master each time.
Does deep zoom work without JavaScript?
The Image API itself is just HTTP, so individual regions are reachable without JS, but smooth pan-and-zoom needs a client like OpenSeadragon. Provide a static full-size fallback link for non-JS users.
Should I store JPEG 2000 or pyramidal TIFF as my source?
Both work. Pyramidal TIFF is faster to decode and simpler to debug; JP2 is smaller on disk and common in heritage repositories. Pick one per collection and document the choice.