# sigal: a Python gallery generator that leaves the lightbox to JavaScript

> sigal walks a directory of images, resizes them, writes thumbnails and renders Jinja2 pages, then hands the viewing experience to a JavaScript gallery library such as colorbox, galleria or photoswipe. The division of labour is deliberate, and it explains both what sigal is good at and where it stops.

**saimn/sigal** — yet another simple static gallery generator

- Repository: https://github.com/saimn/sigal
- Website: https://sigal.readthedocs.io/en/latest/
- Stars: 949 · Forks: 169
- Language: JavaScript
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/saimn-sigal

## A generator that deliberately stops before the gallery

Sigal describes itself, without apology, as yet another simple static gallery generator. It is written in Python and builds a static gallery of images. The feature list in the README is short: process directories recursively, generate HTML pages using Jinja2 templates, use relative links for a portable output, support themes, videos, EXIF tags and zip download, and process in parallel.

What makes the design interesting is the sentence that explains the division of labour. The idea behind Sigal is to ease the use of JavaScript libraries like galleria. Those libraries do a great job of displaying the images, and Sigal does what is missing: resize images, create thumbnails, generate HTML pages.

That is a narrower ambition than most gallery tools have, and it is a coherent one. The hard part of a photo gallery is not the lightbox, it is the derivatives. A gallery with 4000 photos needs resized display images, thumbnails for a contact sheet, and a directory structure that the generated HTML can reference without a web server rewriting paths. Sigal takes on the derivative work and leaves the interaction to a mature JavaScript library.

The intended user is someone with a folder of images who wants a browsable site they can upload anywhere, and who does not want a JavaScript build chain, a Node dependency tree or a headless CMS to get there. The output is static HTML, so it works on any static host.

One oddity is worth noting for anyone filtering repositories by language. The language field of the repository lists JavaScript, while the project itself is written in Python and the README says so explicitly. The classification follows the gallery libraries the themes are built on rather than the generator.

## Python 3.11 and a dependency list you can read at a glance

The packaging metadata is compact, and it says a lot about the project's shape.

The floor is Python 3.11, stated in the README and enforced as requires-python of 3.11 or newer in pyproject.toml. The runtime dependencies are blinker, click, Jinja2 at 2.7 or newer, Markdown, Pillow at 10.0.0 or newer, pilkit and natsort, and the packaging file lists them plainly:

```toml
dependencies = [
    "blinker",
    "click",
    "Jinja2>=2.7",
    "Markdown",
    "Pillow>=10.0.0",
    "pilkit",
    "natsort",
]
```

Two of those deserve comment. Pillow is the imaging library that does the actual resizing, and pilkit is the processing layer that provides the image operations, which is why Sigal does not have to implement thumbnailing itself. natsort is a small but telling dependency: it sorts filenames the way a person expects, so that IMG_2 comes before IMG_10, rather than the way a byte comparison does. For a gallery whose folders are usually named after dates and camera counters, that single dependency removes an entire class of embarrassing output.

Markdown is there because galleries often carry a description or a caption file, and Jinja2 is the template engine that turns the image metadata into pages. blinker and click are the plumbing: click is the command line parser, and blinker is the signal library used for event hooks.

The optional extras are where the project shows its ambition. The all group pulls in brotli and zopfli for compressed output, feedgenerator for an RSS feed of the gallery, cryptography for the encryption plugin, pillow-heif for HEIF images and pdf2image for PDF pages. Separate groups exist for tests, with pytest and pytest-cov, and for docs, with Sphinx, furo and cryptography. The split means the common case installs seven packages and the exotic cases opt in.

## Three themes, three different JavaScript gallery libraries

Sigal ships three themes, and the choice of theme is really a choice of JavaScript gallery library. The themes are based on colorbox, galleria and photoswipe, and the README links a live demo of each.

That choice has consequences. Colorbox is a lightbox in the older jQuery tradition: clicking an image opens an overlay, and the page underneath is the gallery. Galleria is a full screen photo gallery with its own chrome, transitions and thumbnail strip, and it is the library the README names first when it describes the project's purpose. PhotoSwipe is the most modular of the three, with a defined API and a plugin model that lets a site add captions, share buttons or a different transition.

All three are static. None of them needs a server, and that is what makes the output portable.

From a maintenance point of view, shipping these themes means shipping JavaScript libraries of a particular vintage. The README does not state version numbers for them, and the repository does not have them as a separate module of their own, so they live inside the themes directory, which the packaging configuration includes wholesale through the themes glob in package data.

A template is a Jinja2 template, and the directory is shipped with the package. That means a site that wants a different look does not need to fork the generator, only supply its own template directory and point the configuration at it. The cost is that the template has to speak the context the generator provides, which is the part the documentation on the website is for.

## The command line entry point and where to get it

The README does not include an install recipe, so the honest path is the one it points to. The links section gives the latest documentation on the website, the source, issues and pull requests on GitHub, and the releases on PyPI.

The command itself is a console script declared in the packaging metadata, named sigal, and it points at the main function of the package's entry module. The click dependency in the dependency list is what builds the command line interface, which means the subcommands and flags follow the usual click conventions.

For a first use, the shape of the work is short. Point sigal at a directory that contains your images, let it walk that directory recursively, and it produces a gallery folder of resized images, thumbnails and generated HTML. The recursive processing is what makes it useful for a real photo collection, where a shoot is spread over date folders rather than sitting in one directory.

The relative links feature is the other part of a first run worth understanding. The README lists relative links for a portable output as a headline feature, and it follows from the way the generator writes output: the HTML refers to its images by relative path, so the whole gallery folder can be moved, zipped or served from any subdirectory without rewriting anything. Combined with the zip download support, the output is designed to be handed to someone else as a single artefact.

The configuration reference lives in the documentation on the website, since the README does not document the settings keys themselves.

## Parallel processing and a plugin layer

Two mechanisms in the metadata point to how the project handles scale and extension.

The first is parallel processing, which the README lists among the features. A gallery with thousands of images spends most of its time in image decoding and resizing, and that is exactly the kind of work that parallelises cleanly across cores. The blinker dependency fits here too, since signals give the code a way to broadcast that a stage of the pipeline has finished, which is what a parallel build needs in order to move to the next stage.

The second is the plugin system, and its footprint is visible in the packaging configuration. The package data section ships the static assets of the encrypt plugin, under the path plugins/encrypt/static, and the whole themes directory through a glob that covers every template and asset beneath it. The presence of an encrypt plugin with its own static assets means there is an encryption plugin, and the optional dependency group includes cryptography, which is the library an encryption plugin would need. The README feature list does not describe the plugin API, so the extent of the extension surface is one of the things the website documentation is for.

The theme glob, by contrast, ships the whole tree, which is the right call because a theme is a directory of templates and assets rather than a single file.

The remaining optional extras extend the output rather than the process. brotli and zopfli compress the generated files for delivery, feedgenerator produces an RSS feed for a gallery that is updated over time, and pdf2image plus pillow-heif extend the input side to PDF documents and HEIF photographs. That last pair is the pragmatic detail worth checking early, because a camera phone library usually contains HEIF files and a gallery that silently skips them is a common complaint.

## setuptools_scm, a readthedocs build and a pre-commit hook

The build system in pyproject.toml requires setuptools at 64 or newer and setuptools_scm with the toml extra at 3.4 or newer, and the version is dynamic rather than written into the file. The setuptools_scm configuration writes the computed version into src/sigal/version.py at build time, which is the standard pattern for a project that wants a version derived from the git tag instead of a manually maintained string.

The project also uses the src layout, with the package finder pointed at src and namespaces enabled, and it includes package data, which is what allows themes and plugin assets to be installed rather than left in the source tree.

The surrounding tooling is careful in small ways. The pytest configuration sets a minimum version of 9.0, treats warnings as errors apart from ResourceWarning, and restricts test paths to the tests directory. The presence of a pre-commit config at the repository root, together with a git-blame-ignore-revs file, is a common sign of a project that has run a formatter across its history and then told Git to ignore that commit when attributing lines.

Documentation is built with Sphinx and published on readthedocs, with the readthedocs configuration at the root and the docs directory in the tree. There is a tox.ini for multi environment test runs, and an AUTHORS file, which places the project in the tradition of long running Python tools rather than a fresh script.

The license field says MIT, and the README repeats that the project is MIT licensed. The classifiers also carry Development Status 5 - Production/Stable, which is a packaging classification rather than a promise, but it does tell you the author considers the tool finished for practical use.

## A release every year and the case for the command line gallery libraries

On maintenance, the repository is not archived and the last push was on 2026-10-01. The release history is slow and regular: 2.4 on 2024-02-28, 2.5 on 2025-02-25 and 2.6 on 2026-02-06. That is roughly one release a year, and the pattern suggests a stable tool that gets a dependency refresh rather than a fast moving one.

The alternative worth naming is not another generator. It is the option of skipping the generator entirely: put the JavaScript library on a page yourself, point it at your original images, and serve the folder. Galleria, Colorbox and PhotoSwipe all work that way, and if the gallery is small, the derivative step may not be worth automating.

The difference in approach is exactly the step Sigal inserts. Without a generator, the browser downloads the full size original for every image you display, because there are no resized versions to request. A gallery of a few dozen photos survives that. A gallery of a few thousand does not, both because of bandwidth and because the browser decodes full resolution images it will never show at full size.

Sigal's contribution is therefore the derivative pipeline, the folder structure, the relative linking and the generated pages, and its cost is that you now depend on a Python tool, a theme, and a theme's JavaScript library of a particular vintage, and that the output is a set of static files that will drift from the source images unless you re-run the generator.

The right question is not which gallery looks better. It is whether you need derivatives at all.

## Conclusion

sigal suits anyone with a directory of photos who wants a static, self contained gallery with no server side runtime, and it is a poor fit for a site that needs a modern responsive layout, since the viewing layer is a JavaScript library from an era before the grid layout became the default. Verify three things before you adopt it: that your environment runs Python 3.11 or newer, that the theme you want exists among the three bundled ones or that you are willing to write the Jinja2 template yourself, and that the optional extras you need are installed, since HEIF decoding, PDF rendering, brotli and zopfli compression and feed generation all live behind the all extra. A first run is a single sigal command in a directory containing an images folder, and the README points to sigal.readthedocs.io for the configuration reference.

## FAQ

### What is sigal and what does it generate?

Sigal is a static gallery generator written in Python. It processes directories recursively, resizes images, creates thumbnails and generates HTML pages from Jinja2 templates, using relative links so the output stays portable.

### Which Python version does sigal require?

The README states that Sigal requires Python 3.11 or newer, and the pyproject.toml sets requires-python to >=3.11.

### Which themes does sigal ship with?

Three, based on the colorbox, galleria and photoswipe JavaScript libraries, each with a live demo linked from the README. Themes are Jinja2 template directories and are shipped with the package through the themes glob in the package data configuration.

### How do I install sigal?

The README does not give a command line recipe, and points instead to the documentation website, the GitHub repository and the releases on PyPI. The package installs a console script named sigal, which points at the main function of the package entry module.

### Can sigal handle HEIF photos or PDF files?

The optional all group includes pillow-heif and pdf2image, alongside brotli, zopfli, feedgenerator and cryptography, so those input formats need the extra installed rather than the base install.

### How often is sigal released?

The repository is not archived and the last push was on 2026-10-01. Releases are annual, with 2.4 on 2024-02-28, 2.5 on 2025-02-25 and 2.6 on 2026-02-06.

## Sources

- [License: MIT](https://github.com/saimn/sigal/blob/main/LICENSE)
- [Project website](https://sigal.readthedocs.io/en/latest/)
- [README](https://github.com/saimn/sigal/blob/main/README.md)
- [Releases](https://github.com/saimn/sigal/releases)
- [saimn/sigal on GitHub](https://github.com/saimn/sigal)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/saimn-sigal
