A fluent, type-safe image processing library for Gleam, powered by ImageMagick.
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.
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.
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.
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.
The repository includes priv/policy.xml - a whitelist-based ImageMagick security policy that:
- Blocks all formats by default - Only explicitly allowed formats can be processed
- Allows safe formats: PNG, JPEG, WebP, GIF, BMP, TIFF, AVIF, HEIC, PBM, PGM, PPM
- Blocks dangerous formats: PDF, PostScript, MVG, MSL, SVG, XPS, WMF, EMF, and others
- Sets resource limits: 256MB memory, 1GB disk, 30-second timeout, 16K max dimensions
- Disables dangerous features: External command execution, file path expansion, clipboard access, module loading
Option 1: Environment Variable (Recommended for Development)
export MAGICK_CONFIGURE_PATH=/path/to/alakazam/privOption 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 policyOption 3: Docker/Container
COPY priv/policy.xml /etc/ImageMagick-7/policy.xml- Always use a security policy in production environments processing user uploads
- Run in isolated containers with limited resources and network access
- Validate file types before processing (check magic bytes, not just extensions)
- Set file size limits before images reach ImageMagick
- Monitor resource usage and set up alerts for unusual patterns
- Keep ImageMagick updated with the latest security patches
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.jpgRead more about security policys here
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 -versiongleam add alakazam// Basic resize and save
image.from_file("photo.jpg")
|> image.resize_contain(800, 600)
|> image.to_file("resized.jpg")
}// 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")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")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
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")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")
}
}// 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)// 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")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
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
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
filter(image, filter)- Set resampling filter for resizes
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
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
crop(image, x, y, width, height)- Crop to a specific rectangle at position (x, y) with given dimensions
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
strip(image)- Remove all metadata (EXIF, ICC, comments)identify(path)- Get detailed image information
to_command(image, output_path)- Returns the ImageMagick command string without executingraw(image, key, value)- Add custom ImageMagick arguments
- PNG
- JPEG
- BMP
- PBM (Portable Bitmap)
- PGM (Portable GrayMap)
Use Keep format to maintain the original format when converting.
gleam run # Run the project
gleam test # Run the tests
gleam format --check src test # Check formattingFurther documentation can be found at https://hexdocs.pm/alakazam.
This project is licensed under the Apache License 2.0.