digital libraries,
web preservation,
books,
archives.

mkiiif, yet another static IIIF generator

2026-03-19 tags: iiif minimal computing static digital library

I revisited an old Go package I've been using over the past few years to build IIIF manifests — nothing fancy, just some glue around structs and JSON. From that I built a new CLI, mkiiif, to generate IIIF manifests from static images (tiled or not). There are plenty of similar tools out there (iiif-tiler, tile-iiif, biiif, ...) but none quite matched the CLI ergonomics I needed for my daily workflow.

I moved the library to this new repository atomotic/iiif. The tool mkiiif can be installed with Go:

go install github.com/docuverse/iiif/cmd/mkiiif@latest

mkiiif can generate an IIIF manifest from a source directory containing images, or from a PDF file that gets exploded and converted to images via mupdf. Output images can be either untiled or static tiles generated with vips. Both approaches produce a IIIF Level 0 compliant layout, static files that can be served from any HTTP server, with no image server required. Untiled is less efficient for large images but perfectly fine for printed books, papers, and similar material.

mupdf and vips are external dependencies, that need to be installed separately. They are invoked via subprocess; I chose not to add Go library wrappers around them to keep the tool simple. WASM ports of both may become viable in the future.

The CLI usage:

Usage of mkiiif:
  -base string
        Base URL where the manifest will be served (e.g. https://example.org/iiif)
  -destination string
        Output directory; a subdirectory named <id> will be created inside it, containing the images and manifest.json
  -id string
        Unique identifier for the manifest (e.g. book1)
  -resolution int
        Resolution (DPI) used when converting PDF pages to images via mutool (default 150)
  -source string
        Path to a directory of images or a PDF file to convert
  -tiles
        Generate IIIF image tiles for each image using vips dzsave (requires vips)
  -title string
        Human-readable title of the manifest

Example:

~ mkiiif -base https://digital.library.org -destination ./public -id iiif01 -source ~/book.pdf -title "iiif 01"

Or with tiling:

~ mkiiif -base https://digital.library.org -destination ./public -id iiif01 -source ~/book.pdf -title "iiif 01" -tiles

Both commands produce the following structure inside ./public:

└── iiif01
    ├── index.html
    ├── manifest.json
    ├── page-001.png
    ├── page-002.png
    ├── page-....png
    └── page-....png
└── iiif01
│   ├── index.html
│   ├── manifest.json
│   ├── page-001
│   │   ├── 0,0,1024,1024
│   │   │   └── 512,512
│   │   │       └── 0
│   │   │           └── default.jpg
...
│       ├── full
│       │   ├── 362,501
│       │   │   └── 0
│       │   │       └── default.jpg
│       │   └── max
│       │       └── 0
│       │           └── default.jpg
│       └── info.json
...

The directory can then be served from https://digital.library.org.

I've adopted this URL scheme:

https://{base}/{id}
    /manifest.json — the IIIF manifest
    /index.html    — a simple viewer

So in the example above, https://digital.library.org/iiif01 opens a full viewer to browse the object. The viewer used is Triiiceratops — the newest viewer in the IIIF ecosystem. Built on Svelte and OpenSeadragon, is still young, but very usable, lightweight, and easy to embed and customize. It is my favourite viewer.

mkiiif doesn't handle metadata for now (and probably won't) — the manifest can be easily patched to insert descriptive metadata in a later step, after image preparation, pulling from any existing datasource or metadata catalog.

Here is a full working example: https://docuver.se/iiif/p3tgsk8jqt/

A few open questions I haven't fully resolved:

  • The main drawback of generating IIIF this way is that you end up managing a large number of files on the filesystem, and handling millions of small image tiles can be slow (and costly). This is where IIIF intersects — and overlaps — with similar practices in digital preservation, such as BagIt, OCFL, and WARC/WACZ. So far there's no specification or viewer implementation that handles IIIF containers (e.g. a zip file bundling images, tiles, and the manifest). Discussions on this have been ongoing in the past; I've recently been looking at analogous approaches like GeoTIFF and SZI.
  • A static IIIF bundle generated with this CLI still needs to be served from an HTTP server, with the base URL defined at derivation time. Could such a bundle be opened from localhost and viewed directly in the browser? Service Workers might help here (even if HTTP is still needed), but it's a rabbit hole I haven't explored yet.

The CLI is pretty bare-bones — feel free to suggest improvements or report bugs. I've been using it over the past weeks as part of a personal project: an amateur digital library built around a DIY book scanner I assembled at home, to preserve magazines, zines, and similar material (content NSFW and out of scope to link here).