alakazam

Package VersionHex Docs

A fluent, type-safe image processing library for Gleam, powered by ImageMagick.

How It Works

alakazam is a thin, composable wrapper around the ImageMagick magick command-line tool. Rather than binding to a native C library, it builds a pipeline of operations in Gleam and compiles them into a single magick command that is executed when the image is written.

import alakazam/image

pub fn main() {
  image.from_file("photo.jpg")
  |> image.resize_contain(800, 600)
  |> image.sharpen(0.5)
  |> image.to_file("output.jpg")
  // Executes: magick photo.jpg -resize 800x600 -sharpen 0.5 output.jpg
}

You can inspect the generated command at any point using to_command/2, which returns the full command string without executing it — useful for debugging or logging.

Why a CLI wrapper?

No native bindings. There is no FFI layer, no platform-specific compilation, and no memory safety concerns at the binding boundary. The library is pure Gleam; only the magick binary is native.

Battle-tested engine. ImageMagick has over 30 years of development behind it. Format quirks, ICC profiles, EXIF handling, and hundreds of other edge cases are handled by a mature, widely-deployed tool rather than a new binding.

Transparent and debuggable. Because the library produces a plain shell command, you can inspect exactly what will run with to_command/2 and paste it directly into a terminal to reproduce or investigate any result.

Full feature access. The raw/3 escape hatch lets you pass any ImageMagick option the library does not explicitly wrap, so you are never blocked by a missing API.

Tradeoffs

Each pipeline execution spawns an OS process. This is well-suited for batch processing, image pipelines, and server-side generation, but is not appropriate for tight loops that process many small images per second.

Security. ImageMagick has a history of security vulnerabilities related to parsing complex image formats. Processing untrusted user uploads directly can be risky. See the Security section for detailed recommendations.

ImageMagick must also be installed on every host that runs your application. See the Prerequisites section for installation instructions.

Security

ImageMagick is a powerful tool that can execute complex operations on images. When processing untrusted user uploads, security is critical. This library includes a restrictive security policy to minimize attack surface.

The policy is not enabled by default.

Security Policy

The repository includes priv/policy.xml - a whitelist-based ImageMagick security policy that:

Using the Security Policy

Option 1: Environment Variable (Recommended for Development)

export MAGICK_CONFIGURE_PATH=/path/to/alakazam/priv

Option 2: System-wide Installation

Copy the policy to your ImageMagick configuration directory:

# macOS (Homebrew)
cp priv/policy.xml /usr/local/etc/ImageMagick-7/policy.xml

# Ubuntu/Debian
sudo cp priv/policy.xml /etc/ImageMagick-7/policy.xml

# Verify the policy is loaded
magick -list policy

Option 3: Docker/Container

COPY priv/policy.xml /etc/ImageMagick-7/policy.xml

Production Security Recommendations

  1. Always use a security policy in production environments processing user uploads
  2. Run in isolated containers with limited resources and network access
  3. Validate file types before processing (check magic bytes, not just extensions)
  4. Set file size limits before images reach ImageMagick
  5. Monitor resource usage and set up alerts for unusual patterns
  6. Keep ImageMagick updated with the latest security patches

Testing the Policy

To verify the policy is working:

# This should fail (PDF is blocked)
magick document.pdf output.png
# Error: attempt to perform an operation not allowed by the security policy

# This should succeed (PNG is allowed)
magick image.png output.jpg

Read more about security policys here

Prerequisites

ImageMagick must be installed and the magick command must be available in your PATH.

# macOS
brew install imagemagick

# Ubuntu/Debian
apt-get install imagemagick

# Verify installation
magick -version

Installation

gleam add alakazam

Quick Start

// Basic resize and save
image.from_file("photo.jpg")
|> image.resize_contain(800, 600)
|> image.to_file("resized.jpg")
}

Usage Examples

Resizing Images

// Fit within dimensions (preserves aspect ratio)
image.from_file("large.png")
|> image.resize_contain(300, 200)
|> image.to_file("fitted.png")

// Fill exact dimensions (may stretch)
image.from_file("photo.jpg")
|> image.resize_fill(800, 600)
|> image.to_file("filled.jpg")

// Cover/crop to fill (CSS object-fit: cover behavior)
image.from_file("banner.jpg")
|> image.resize_cover(1920, 1080, image.Center)
|> image.to_file("cover.jpg")

// Reduce colors with dithering for retro/pixel art look
image.from_file("photo.jpg")
|> image.dither()
|> image.colors(8)
|> image.to_file("retro.png")

Creating Thumbnails

Thumbnails are optimized for creating preview images - they automatically strip metadata and use less memory:

image.from_file("high_res_photo.jpg")
|> image.thumbnail(150, 150)
|> image.to_file("thumb.jpg")

Controlling Resampling Quality

Choose the right filter for your image type:

// For pixel art - preserve sharp edges
image.from_file("pixel_art.png")
|> image.filter(image.Nearest)
|> image.resize_contain(200, 200)
|> image.to_file("scaled.png")

// For photos - high quality (default)
image.from_file("photo.jpg")
|> image.filter(image.Lanczos)
|> image.thumbnail(100, 100)
|> image.to_file("thumb.jpg")

Available filters:

Chaining Operations

Combine multiple transformations in a single pipeline:

image.from_file("input.jpg")
|> image.resize_contain(800, 600)
|> image.sharpen(0.5)
|> image.strip()  // Remove metadata for smaller files
|> image.to_file("optimized.jpg")

Getting Image Information

import alakazam/image

pub fn main() {
  case image.identify("photo.jpg") {
    Ok(info) -> {
      io.println("Format: " <> format_to_string(info.format))
      io.println("Dimensions: " <> int.to_string(info.width) <> "x" <> int.to_string(info.height))
      io.println("Colorspace: " <> colorspace_to_string(info.colorspace))
      io.println("Bit depth: " <> int.to_string(info.depth))
      io.println("File size: " <> int.to_string(info.file_size) <> " bytes")
      io.println("Has alpha: " <> bool.to_string(info.has_alpha))
    }
    Error(e) -> io.println("Failed to identify image")
  }
}

Format Conversion

// Convert PNG to JPEG
image.from_file("image.png")
|> image.to_file("image.jpg")

// Get image as bytes in specific format
image.from_file("photo.jpg")
|> image.to_bits(image.Png)

Working with Binary Data

// Load image from BitArray (e.g., from database or API)
let image_bits = read_image_from_database()
image.from_bits(image_bits)
|> image.resize_contain(100, 100)
|> image.to_file("resized.png")

// Round-trip: File -> BitArray -> File
image.from_file("photo.jpg")
|> image.to_bits(image.Png)
|> image.from_bits
|> image.to_file("converted.png")

Color Reduction

Reduce the color palette for stylistic effects or smaller file sizes. Both operations work well with dither() to smooth gradients.

colors(n) - Reduces to n total colors using intelligent quantization. ImageMagick analyzes the image and picks the best colors to represent it.

// 8-color image with dithering for smooth gradients
image.from_file("photo.jpg")
|> image.dither()
|> image.colors(8)
|> image.to_file("8color.png")

posterize(n) - Reduces to n levels per color channel (R, G, B). Creates total colors with uniform steps, producing visible color banding (posterization effect).

// 4 levels per channel = 4³ = 64 total colors (retro poster look)
image.from_file("photo.jpg")
|> image.dither()
|> image.posterize(4)
|> image.to_file("posterized.png")

// Extreme posterization: 2 levels per channel = only 8 colors total
image.from_file("photo.jpg")
|> image.dither()
|> image.posterize(2)
|> image.to_file("8color-poster.png")

When to use:

Available Operations

Loading & Saving

Resizing

Quality Control

Effects

Transformations

Cropping

Colors & Color Space

Metadata

Utilities

Supported Formats

Use Keep format to maintain the original format when converting.

Development

gleam run   # Run the project
gleam test  # Run the tests
gleam format --check src test  # Check formatting

Documentation

Further documentation can be found at https://hexdocs.pm/alakazam.

License

This project is licensed under the Apache License 2.0.