Margarine ๐Ÿงˆ

Margarine Logo

AI-powered image generation for Elixir using FLUX and SDXL.

Margarine brings state-of-the-art text-to-image and image-to-image generation to the Elixir ecosystem with a clean, native API. Generate beautiful images from text prompts or transform existing images with just a few lines of code.

Features

Quick Start

# Add to mix.exs
def deps do
[
{:margarine, "~> 0.2.0"},
# REQUIRED: Choose ONE Nx backend based on your hardware
{:emlx, "~> 0.1"} # For Apple Silicon (M1/M2/M3/M4)
# {:exla, "~> 0.10"} # For NVIDIA/AMD GPU or CPU
]
end

Important: You must install either EMLX or EXLA alongside Margarine. The backend handles GPU/CPU acceleration for tensor operations.

# Simple text-to-image with FLUX
{:ok, image} = Margarine.generate("a red panda eating bamboo")
Margarine.Image.save(image, "panda.png")
# Photorealistic with SDXL
{:ok, image} = Margarine.generate("a serene mountain landscape at sunset",
model: :sdxl_turbo, # Fast photorealistic generation
steps: 1
)
# Image-to-image transformation
{:ok, image} = Margarine.img2img(
"turn into a watercolor painting",
"photo.png",
denoising_strength: 0.5, # How much to change (0.0-1.0)
model: :sdxl_base
)

The best way to learn Margarine is through our interactive Livebook tutorials. These step-by-step guides run live code in your browser and let you experiment as you learn.

Start Here: Choose Your Model

We have two comprehensive tutorials, one for each model type:

๐Ÿ“˜ FLUX Tutorial (Artistic Generation)

livebook open notebooks/flux_getting_started.livemd

You'll learn:

Best for: Illustrations, artistic images, creative concepts

๐Ÿ“— SDXL Tutorial (Photorealistic Generation)

livebook open notebooks/sdxl_getting_started.livemd

You'll learn:

Best for: Realistic photos, detailed scenes, portraits

First Time Using Livebook?

Install it first:

mix escript.install hex livebook

Then open either tutorial above. The notebooks include everything you need, including dependency installation!

Installation

1. Add Dependencies

Add margarine and an Nx backend to your mix.exs:

def deps do
[
{:margarine, "~> 0.1.0"},
# Choose ONE backend:
{:emlx, "~> 0.1"} # Apple Silicon (M1/M2/M3/M4) - Recommended for Macs
# {:exla, "~> 0.10"} # NVIDIA/AMD GPU or CPU
# {:torchx, "~> 0.7"} # PyTorch backend (experimental)
]
end

2. Configure Nx Backend

In config/config.exs:

# For Apple Silicon
config :nx,
default_backend: EMLX.Backend,
default_defn_options: [compiler: EMLX]
# OR for NVIDIA/AMD GPU
# config :nx,
# default_backend: EXLA.Backend,
# default_defn_options: [compiler: EXLA]

For NVIDIA GPU users on Linux: See the EXLA Setup Guide for Linux (NVIDIA GPU) for detailed CUDA installation and configuration instructions.

3. First Run Setup

No manual installation required! Margarine automatically handles everything:

  1. UV Package Manager - Automatically downloaded and installed by Pythonx
  2. Python 3.11+ - Downloaded via UV (~100MB)
  3. PyTorch & Dependencies - Installed via UV (~500MB)
  4. Everything cached - Instant subsequent runs

This takes 2-5 minutes on first run. All you need is an internet connection and ~15GB disk space.

For production deployments, we recommend running a "warm-up" generation when your server starts:

# In your application startup
defmodule MyApp.Application do
def start(_type, _args) do
# Warm up Margarine to avoid first-request timeout
Task.start(fn ->
Margarine.check_environment()
end)
# ... rest of your application setup
end
end

System Requirements

Minimum Requirements

Model Memory Requirements

Usage Examples

Basic Generation

# Simple prompt
{:ok, image} = Margarine.generate("a cute red panda")
# Check the result
IO.inspect(Nx.shape(image)) # {1024, 1024, 3}
IO.inspect(Nx.type(image)) # {:u, 8} (RGB values 0-255)
# Save to file
Margarine.Image.save(image, "panda.png")

Reproducible Generation

# Use a seed for reproducible results
opts = [seed: 42, model: :flux_schnell]
{:ok, image1} = Margarine.generate("a mountain landscape", opts)
{:ok, image2} = Margarine.generate("a mountain landscape", opts)
# image1 and image2 will be identical

High-Quality Generation

# Use FLUX Dev for artistic quality
{:ok, image} = Margarine.generate("a cyberpunk cityscape",
model: :flux_dev,
steps: 28,
guidance_scale: 3.5,
size: {1024, 1024}
)
# Use SDXL Base for photorealistic quality
{:ok, image} = Margarine.generate("a photorealistic portrait",
model: :sdxl_base,
steps: 20,
guidance_scale: 7.5,
size: {1024, 1024}
)

Image-to-Image Transformation

# Subtle style change (keep most of original)
{:ok, image} = Margarine.img2img(
"watercolor painting style",
"photo.png",
denoising_strength: 0.3,
model: :sdxl_base
)
# Moderate transformation
{:ok, image} = Margarine.img2img(
"oil painting, impressionist style",
"photo.png",
denoising_strength: 0.6,
model: :flux_schnell
)
# Heavy modification (only rough composition remains)
{:ok, image} = Margarine.img2img(
"cyberpunk city at night",
"photo.png",
denoising_strength: 0.8,
model: :sdxl_base
)

Error Handling

case Margarine.generate(prompt, opts) do
{:ok, image} ->
Margarine.Image.save(image, "output.png")
IO.puts("Image generated successfully!")
{:error, reason} ->
IO.puts("Generation failed: #{reason}")
end

Health Checks

# Check if Python environment is ready
env = Margarine.check_environment()
case env do
%{pythonx_initialized: true, python_version: version} ->
IO.puts("Ready! Python #{version}")
%{pythonx_initialized: false} ->
IO.puts("Python not initialized yet")
end

Configuration Options

Text-to-Image Options (Margarine.generate/2)

OptionTypeDefaultDescription
:model:flux_schnell | :flux_dev | :sdxl_turbo | :sdxl_base:flux_schnellModel to use
:stepspos_integer()Model-specificNumber of denoising steps (4 for schnell, 28 for dev, 1 for turbo, 20 for base)
:guidance_scalefloat()Model-specificGuidance strength (0.0 for schnell/turbo, 3.5 for dev, 7.5 for base)
:seedinteger() | nilnil (random)Random seed for reproducibility
:size{height, width}{1024, 1024}Image dimensions (must be divisible by 8)

Image-to-Image Options (Margarine.img2img/3)

OptionTypeDefaultDescription
:model:flux_schnell | :flux_dev | :sdxl_turbo | :sdxl_base:flux_schnellModel to use
:denoising_strengthfloat() (0.0-1.0)0.75How much to modify (0.0 = no change, 1.0 = complete regeneration)
:stepspos_integer()Model-specificNumber of denoising steps
:guidance_scalefloat()Model-specificGuidance strength
:seedinteger() | nilnil (random)Random seed for reproducibility
:size{height, width} | nilAuto-detect from imageTarget dimensions (defaults to original image size, rounded to multiple of 8)

Architecture

Margarine uses a modular architecture:

All Python dependencies are managed automatically via UV. No manual pip install or virtualenv management required!

Development

This project follows strict Test-Driven Development (TDD) practices.

Running Tests

# Fast unit tests (default)
mix test
# With coverage report
mix test --cover
# Integration tests (requires model download, ~12GB)
mix test --only integration
# All tests including integration
mix test --include integration
# Linting
mix credo --strict

Test Coverage

We maintain 80%+ code coverage. Current coverage: 75.3%

Contributing

We welcome contributions! Please:

  1. Write tests first (TDD)
  2. Ensure all tests pass (mix test)
  3. Run Credo (mix credo --strict)
  4. Update documentation as needed

Roadmap

Phase 1: FLUX MVP โœ… (v0.1.0)

Phase 2: SDXL + Image-to-Image โœ… (v0.2.0)

Phase 3: Inpainting (Planned)

Phase 4: Advanced Features (Future)

Troubleshooting

"Python environment not initialized"

The first run takes 2-5 minutes to download Python and dependencies. Subsequent runs are instant.

Out of Memory Errors

Slow Generation

Documentation

License

MIT License - see LICENSE for details.

Acknowledgments


Built with โค๏ธ by the Elixir community. Logo generated with FLUX - a perfect example of what this library can do!