# Introduction `ol.vips` wraps libvips’s image graph in [Closeable](https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/io/Closeable.html) Clojure values. Loader functions such as `v/from-file`, `v/from-buffer`, and `v/from-stream` return image handles, and operations such as `ops/resize`, `ops/colourspace`, and `v/call` return new handles rather than mutating the original image. This model is immutable and pipeline-oriented. In the common case, libvips reads only enough of the input to discover image metadata and build an operation graph. Calling loaders and transforms usually does not decode the whole image up front. libvips is demand-driven, so the pixel work happens when something downstream asks for image data. In `ol.vips`, that usually means a sink such as `v/write-to-file`, `v/write-to-buffer`, or `v/write-to-stream`. At that point libvips pulls pixels through the pipeline, processing small regions with a horizontally threaded execution model instead of materializing every intermediate image in memory. Some operations that need actual pixel values can trigger evaluation earlier, but metadata queries such as `v/width`, `v/height`, and `v/metadata` can often be satisfied from loader metadata alone. Because the image graph is immutable and operations are generally side-effect free, libvips can also reuse and cache operation results internally. `ol.vips` adds a small amount of Clojure-specific structure on top of that model. The `ol.vips.operations` namespace is generated from libvips introspection data, `ol.vips.enums` exposes the enum keywords used by many options, and the top-level `ol.vips` namespace provides the file, buffer, stream, metadata, and convenience helpers you use most often. ## Why libvips? The typical libvips workflow is exactly what many applications need: load a large source image, resize or transform it, and write a smaller web-friendly JPEG, PNG, WebP, AVIF, or TIFF result. `ol.vips` gives Clojure access to that model without shelling out to external image tools or spawning child processes. libvips is https://github.com/libvips/libvips/wiki/Why-is-libvips-quick[blazing fast] and https://github.com/libvips/libvips/wiki/Speed-and-memory-use[memory efficient] because it usually works on small regions of uncompressed image data at a time, takes advantage of multiple CPU cores, and avoids materializing every intermediate result in memory. That execution model is a strong fit for image pipelines, thumbnails, upload processing, preprocessing for vision workloads, and other server-side image tasks. It also brings broad format and operation support: [300 operations](https://www.libvips.org/API/current/func-list.html) covering arithmetic, histograms, convolution, morphology, colour, resampling, statistics, and more, with correct handling of colour spaces, embedded ICC profiles, and alpha channels. [libvips](https://github.com/libvips/libvips) was originally created at Birkbeck College and is currently maintained by [John Cupitt](https://github.com/jcupitt) and other contributors. ### Bindings Generation libvips itself is very amenable to automatic bindings generation. The core of `ol.vips` is small introspection layer that loads the native library then introspects it to generate `ol.vips.operations` and `ol.vips.enums`, and then a small runtime for loading/reading/writing. As a convenience for Clojure users there are separate jars available with the native libraries per-platform, but `ol.vips` it self does not depend on them directly. You can depend on one (or more) of those native deps, or provide your own libvips build with `-Dol.vips.native.preload`. You’ll need to choose one. The native companion jars bundle libvips 8.18.6 from `sharp-libvips` 1.3.3. The test suite runs against that bundle. This means that you can even use a custom libvips build or a newer version of libvips without having to wait for `ol.vips` to catch up (though I expect to keep it up to date). Any new or updated operations in libvips can be used with this library using the lower level `ol.vips/call` function. You can even generate the bindings yourself if you need to, simply use `ol.vips.codegen`. ## Going to Production See [Going to Production](https://docs.outskirtslabs.com/ol.vips/next/going-to-production) for production guidance on pipeline shape, sequential access, untrusted inputs, sanity checks, allocator tuning, and cache settings in `ol.vips`. See [Examples](examples.adoc) for runnable snippets that cover file, buffer, stream, metadata, thumbnailing, transforms, composition, and animated-image workflows. ## Loading the native library `ol.vips` uses this procedure to load the native components: 1. Load any explicit libraries listed in `-Dol.vips.native.preload`. 2. Load the extracted libraries from the platform native jar on the classpath. 3. If that fails, fall back to the system library loader, which can resolve `libvips` from `LD_LIBRARY_PATH`. ### 1. Preload `-Dol.vips.native.preload` is for exact native library file paths. Use it when you want to point `ol.vips` at a specific custom build in a non-standard location, or when you need to preload dependency libraries before libvips itself. On Nix-like systems that can include things like `libstdc{plus}{plus}.so.6` as well as your `libvips` library. The value is a single string containing one or more full library file paths separated by the OS path separator, which is `:` on Linux and macOS and `;` on Windows. If `ol.vips.native.preload` is set, those entries are always loaded first. If any preload entry fails to load, the packaged native-jar path is abandoned for that initialization attempt and `ol.vips` falls back to the system library loader instead of continuing with the native jar. ### 2. Classpath native bundle `ol.vips` detects the current platform, builds a resource path like `ol/vips/native/-` on macOS and Windows or `ol/vips/native/--` on Linux, and then looks for `manifest.edn` under that path. For example, Linux x86-64 glibc resolves to `ol/vips/native/linux-x86-64-gnu/manifest.edn`. If that resource is present, `ol.vips` reads the manifest, extracts the bundled native libraries into the local cache, and loads those extracted files. You can override parts of that platform detection with `-Dol.vips.native.platform-id`, `-Dol.vips.native.os`, `-Dol.vips.native.arch`, and `-Dol.vips.native.libc`. In practice, this means the companion jar for the current runtime platform needs to be on the classpath, meaning the current OS, CPU architecture, and Linux libc when applicable. This path is attempted after any explicit `ol.vips.native.preload` entries. In the successful packaged case, `v/init!` will report `:native-load-source :packaged` and expose the extracted primary library path in `:primary-library-path`, which is useful when debugging exactly what got loaded. ### 3. System fallback The system-library fallback loads by library name rather than full file path. Use this when you want the OS or JVM loader to resolve libvips from `LD_LIBRARY_PATH`, standard system locations, or other platform-specific loader configuration. A platform native jar is not required for this path; if the packaged load fails, including because no matching native jar is present on the classpath, `ol.vips` can still fall back to the system loader. By default the system fallback tries `vips-cpp` and then `vips`. Most users should not need to change this. `-Dol.vips.native.system-libs` exists for the uncommon case where your environment exposes libvips under different system library names. Like `ol.vips.native.preload`, it accepts multiple entries separated by the OS path separator. The fallback only applies to native library loading. If the packaged libraries load successfully but `vips_init` fails afterwards, `ol.vips` does not automatically retry via the system loader. For debugging, `v/init!` exposes runtime state including whether initialization came from the packaged path or the system fallback.