alakazam
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. For those workloads, consider batching operations or pre-generating assets.
ImageMagick must also be installed on every host that runs your application. See the Prerequisites section for installation instructions.
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 -versionInstallation
gleam add alakazamQuick 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:
Lanczos- High quality, sharp results (best for photos)Bicubic- Good balance of quality and speedNearest- Fast, pixelated (best for pixel art)Mitchell- Smooth, good for enlargingTriangle- Fast, simple interpolationCatrom- Sharp edges, good for text
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 n³ 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:
colors()- When you want a specific small palette optimized for the image (e.g., 8-color GIF)posterize()- When you want visible color banding/retro poster effects with uniform color steps
Available Operations
Loading & Saving
from_file(path)- Load image from filefrom_bits(bits)- Load image from BitArray (auto-detects format)to_file(image, path)- Save image to fileto_bits(image, format)- Get image as BitArray
Resizing
resize(image, kind)- General resize with Resize typeresize_contain(image, width, height)- Fit within bounds (CSS: contain)resize_fill(image, width, height)- Exact dimensions (CSS: fill)resize_cover(image, width, height, gravity)- Cover with crop (CSS: cover)thumbnail(image, width, height)- Optimized for previews
Quality Control
filter(image, filter)- Set resampling filter for resizes
Effects
blur(image, radius)- Gaussian blursharpen(image, radius)- Sharpen imagemonochrome(image)- Convert to black and whitenegate(image)- Invert colorsdither(image)- Enable Floyd-Steinberg error-diffusion ditheringordered_dither(image, pattern)- Apply ordered dithering patternscolors(image, num)- Reduce colors (use withdither()for smooth results)posterize(image, levels)- Reduce color levels per channel
Transformations
flip(image)- Vertical mirrorflop(image)- Horizontal mirrorgravity(image, gravity)- Set crop/resize anchor pointextent(image, width, height)- Extend/crop to exact sizeauto_orient(image)- Auto-rotate based on EXIF
Cropping
crop(image, x, y, width, height)- Crop to a specific rectangle at position (x, y) with given dimensions
Colors & Color Space
colorspace(image, kind)- Convert colorspace (acceptsColorspacetype)auto_level(image)- Auto-adjust levelsnormalize(image)- Normalize (enhance contrast by stretching intensity range)background(image, color)- Set background coloralpha_to_image(image)- Extract alpha channel
Metadata
strip(image)- Remove all metadata (EXIF, ICC, comments)identify(path)- Get detailed image information
Utilities
to_command(image, output_path)- Returns the ImageMagick command string without executingraw(image, key, value)- Add custom ImageMagick arguments
Supported Formats
- PNG
- JPEG
- BMP
- PBM (Portable Bitmap)
- PGM (Portable GrayMap)
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 formattingDocumentation
Further documentation can be found at https://hexdocs.pm/alakazam.
License
This project is licensed under the Apache License 2.0.