A high-performance image and video pixelation tool written in Rust with WebAssembly support. Transform images and videos into pixel art with customizable palettes, dithering algorithms, and edge detection.
| Original (6000×4000) | Pixelated (6000×4000) - Floyd-Steinberg, Pico8, Factor 4 |
|---|---|
📸 View Full Gallery - See more examples with different dithering algorithms
- Multiple Dithering Algorithms: Bayer (ordered), Floyd-Steinberg (error diffusion), Riemersma (Hilbert curve)
- Color Palettes: Pico8 (16 colors), custom palettes, or automatic quantization
- Edge Detection: Sobel-based downsampling for detail preservation
- Configurable Parameters: Factor, depth, alpha blending, upscaling
- CLI tool with extensive options
- Batch processing support
- PNG output with transparency support
- Adjustable brightness and contrast
- Full video conversion to pixel art
- FFmpeg integration for frame extraction and encoding
- Progress tracking for long videos
- H.264 output with configurable quality
- Interactive browser-based editor
- Real-time preview
- Undo/redo history (up to 50 states)
- Material Design 3 UI
- WebAssembly-powered processing
- Drag-and-drop image loading
Try it live: Start the web server and open http://localhost:8080
cd web-serve
npm install
npm startSee WEB_GUI_USAGE.md for detailed usage guide.
- Rust 1.70 or newer
- Cargo (comes with Rust)
- FFmpeg (for video frame extraction and encoding)
# Ubuntu/Debian
sudo apt install ffmpeg
# macOS
brew install ffmpeg
# Windows
# Download from https://ffmpeg.org/download.html- Node.js 16+ and npm
- wasm-pack
# Install wasm-pack
cargo install wasm-pack# Clone the repository
git clone https://github.com/muhammad1438/pixelateR.git
cd pixelateR
# Build CLI tool (release mode)
cargo build --release
# The binary will be at: ./target/release/pixelate_r# Build WASM module
cd wasm
wasm-pack build --target web --release
cd ..
# Install and build web frontend
cd web
npm install
npm run build
cd ..
# Serve the web interface
cd web-serve
npm install
npm start
# Open http://localhost:8080# Simple pixelation with Pico8 palette
./target/release/pixelate_r input.png output.png \
--palette pico8 \
--dither bayer \
--factor 6
# Floyd-Steinberg dithering with upscaling
./target/release/pixelate_r input.png output.png \
--palette pico8 \
--dither floyd \
--factor 6 \
--upscale 6,6
# Custom settings with Sobel edge detection
./target/release/pixelate_r input.png output.png \
--palette pico8 \
--dither floyd \
--factor 1 \
--depth 1 \
--sobel 2 \
--alpha 0.65Options:
-p, --palette <PALETTE> Color palette (pico8, or custom)
-d, --dither <DITHER> Dithering algorithm (bayer, floyd, riemersma)
-f, --factor <FACTOR> Downsampling factor (default: 6)
--depth <DEPTH> Downsampling iterations (default: 0)
--sobel <SIZE> Sobel kernel size for edge detection (2-5)
--alpha <ALPHA> Alpha blending (0.0-1.0, default: 0.5)
--upscale <W,H> Upscale dimensions (e.g., "6,6")
--brightness <VALUE> Brightness adjustment (-100 to 100)
--contrast <VALUE> Contrast adjustment (-100 to 100)
--quiet Suppress progress output
-h, --help Print help
# Extract frames from video
mkdir -p /tmp/pixelate-video
ffmpeg -i input.mp4 -vf "scale=640:360" /tmp/pixelate-video/frame_%05d.png
# Process all frames
mkdir -p /tmp/pixelate-output
for f in /tmp/pixelate-video/frame_*.png; do
out="/tmp/pixelate-output/$(basename "$f")"
./target/release/pixelate_r "$f" "$out" \
--palette pico8 \
--dither floyd \
--factor 6 \
--upscale 6,6 \
--depth 0 \
--quiet
done
# Reassemble video
ffmpeg -framerate 24 -i /tmp/pixelate-output/frame_%05d.png \
-c:v libx264 -preset medium -crf 18 -pix_fmt yuv420p \
output-pixelated.mp4See docs/VIDEO_PROCESSING_APPROACH.md for detailed video processing guide.
- Start the web server:
cd web-serve
npm start-
Open http://localhost:8080 in your browser
-
Drag and drop an image or use the file picker
-
Adjust settings:
- Palette: Choose Pico8 or other palettes
- Dither: Select dithering algorithm
- Factor: Adjust pixelation level
- Sobel: Enable edge detection
- Brightness/Contrast: Fine-tune appearance
-
Click "Transform" to apply changes
-
Use undo/redo buttons to navigate history
-
Right-click the canvas and "Save image as..." to download
| Original | Bayer Dithering | Floyd-Steinberg |
|---|---|---|
Sample videos are available in examples/output/:
big-buck-bunny-pixelated-full.mp4- Bayer dithering, 636×360big-buck-bunny-floyd-pico8.mp4- Floyd-Steinberg, 636×360big-buck-bunny-custom.mp4- Custom settings, 320×180
Note: These files are large (122-245MB) and excluded from git via .gitignore.
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#7C3AED','primaryTextColor':'#fff','primaryBorderColor':'#5B21B6','lineColor':'#6B7280','secondaryColor':'#10B981','tertiaryColor':'#F59E0B'}}}%%
graph TB
subgraph "Input Layer"
IMG[Image File]
VID[Video File]
WEB[Web Upload]
end
subgraph "CLI Tool"
CLI[CLI Parser<br/>src/cli/]
LOAD[Image Loader<br/>src/image/]
end
subgraph "Core Processing"
DOWN[Sobel Downsampling<br/>src/filter/sobel.rs]
PAL[Palette Selection<br/>src/color/palette.rs]
QUANT[Color Quantization<br/>src/color/quantize.rs]
DITH[Dithering<br/>src/dither/]
FILT[Filters<br/>src/filter/]
end
subgraph "Output Layer"
SAVE[Save Image<br/>PNG/JPG]
VIDEO[FFmpeg Encode<br/>MP4]
CANVAS[Canvas Display]
end
subgraph "Web Interface"
WASM[WASM Bindings<br/>wasm/src/lib.rs]
TS[TypeScript UI<br/>web/src/main.ts]
end
IMG --> CLI
VID --> CLI
WEB --> TS
CLI --> LOAD
TS --> WASM
LOAD --> DOWN
WASM --> DOWN
DOWN --> PAL
PAL --> QUANT
QUANT --> DITH
DITH --> FILT
FILT --> SAVE
FILT --> VIDEO
FILT --> CANVAS
style DOWN fill:#7C3AED,stroke:#5B21B6,stroke-width:2px,color:#fff
style DITH fill:#7C3AED,stroke:#5B21B6,stroke-width:2px,color:#fff
style WASM fill:#10B981,stroke:#059669,stroke-width:2px,color:#fff
style TS fill:#10B981,stroke:#059669,stroke-width:2px,color:#fff
sequenceDiagram
participant User
participant CLI
participant Loader
participant Sobel
participant Palette
participant Dither
participant Output
User->>CLI: pixelate_r input.png --palette pico8 --dither floyd
CLI->>Loader: Load image
Loader-->>CLI: RGB/RGBA ImageData
CLI->>Sobel: Downsample with edge detection
Note over Sobel: factor=6, depth=0<br/>sobel kernel=3x3
Sobel-->>CLI: Downsampled image
CLI->>Palette: Select Pico8 palette (16 colors)
Palette-->>CLI: Color palette
CLI->>Dither: Apply Floyd-Steinberg
Note over Dither: Error diffusion:<br/>7/16, 3/16, 5/16, 1/16
Dither-->>CLI: Dithered image
CLI->>Output: Upscale & Save
Note over Output: Nearest-neighbor<br/>upscale=6x6
Output-->>User: output.png (636×360)
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#7C3AED','primaryTextColor':'#fff','primaryBorderColor':'#5B21B6','lineColor':'#6B7280','secondaryColor':'#10B981','tertiaryColor':'#F59E0B'}}}%%
flowchart LR
A[Input Video<br/>1280×720 MP4] --> B[FFmpeg Extract]
B --> C[Frame 00001.png<br/>Frame 00002.png<br/>...<br/>Frame 14315.png]
C --> D{Process Each Frame}
D --> E[Downsample<br/>Sobel + Factor]
E --> F[Palette<br/>Pico8 16 colors]
F --> G[Dither<br/>Floyd-Steinberg]
G --> H[Upscale<br/>6×6 Nearest]
H --> I[Processed Frames<br/>636×360]
I --> J[FFmpeg Encode<br/>H.264 CRF 18]
J --> K[Output Video<br/>636×360 MP4]
style D fill:#F59E0B,stroke:#D97706,stroke-width:2px,color:#000
style G fill:#7C3AED,stroke:#5B21B6,stroke-width:2px,color:#fff
style J fill:#10B981,stroke:#059669,stroke-width:2px,color:#fff
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#7C3AED','primaryTextColor':'#fff','primaryBorderColor':'#5B21B6','lineColor':'#6B7280','secondaryColor':'#10B981','tertiaryColor':'#F59E0B'}}}%%
graph LR
subgraph Browser
UI[HTML Canvas<br/>web/index.html]
TS[TypeScript<br/>main.ts]
ID[ImageData<br/>RGBA Buffer]
end
subgraph "WebAssembly"
WASM[WASM Module<br/>wasm/pkg/]
RUST[Rust Core<br/>lib.rs]
end
subgraph "Processing"
PROC[PixelateR<br/>Processing Pipeline]
end
UI -->|User Upload| TS
TS -->|getImageData| ID
ID -->|Uint8Array| WASM
WASM -->|transform| RUST
RUST -->|Process| PROC
PROC -->|RGBA Buffer| RUST
RUST -->|Vec u8| WASM
WASM -->|Uint8Array| TS
TS -->|putImageData| UI
style WASM fill:#10B981,stroke:#059669,stroke-width:2px,color:#fff
style PROC fill:#7C3AED,stroke:#5B21B6,stroke-width:2px,color:#fff
PixelateR/
├── src/ # Rust CLI implementation
│ ├── cli/ # Command-line interface
│ ├── color/ # Color quantization and palettes
│ ├── dither/ # Dithering algorithms (Bayer, Floyd-Steinberg, Riemersma)
│ ├── filter/ # Brightness, contrast, halftone
│ └── video/ # FFmpeg wrapper (future)
├── wasm/ # WebAssembly bindings
├── web/ # TypeScript/Vite frontend
│ ├── src/
│ │ └── main.ts # Main application logic
│ └── index.html # Web interface
├── web-serve/ # Development server
└── examples/ # Sample media and outputs
- Uses 4×4 Bayer matrix
- Fast and consistent patterns
- Good for retro pixel art aesthetic
- Distributes quantization error to neighboring pixels
- Higher quality, more natural gradients
- Larger file sizes due to detail
- Space-filling curve traversal
- Unique texture, good for artistic effects
- Available via halftone filter
- 16-color fantasy console palette
- Vibrant, retro aesthetic
- Default palette for quick results
- Define your own color sets
- Euclidean distance matching in RGB space
The web interface uses Rust compiled to WebAssembly for high-performance image processing in the browser:
- Image loaded via Canvas API
- ImageData (RGBA) sent to WASM
- Rust processes with selected settings
- RGBA buffer returned to JavaScript
- Canvas updated with result
See docs/WEB_GUI_USAGE.md for implementation details.
Processing a 1920×1080 image:
| Algorithm | Time | Settings |
|---|---|---|
| Bayer | ~80ms | factor=6, upscale=6×6 |
| Floyd-Steinberg | ~250ms | factor=6, upscale=6×6 |
| Riemersma | ~400ms | factor=6 |
Benchmarks on AMD Ryzen 5 3600, single-threaded
Processing Big Buck Bunny (9:56, 1280×720, 24fps, 14,315 frames):
- Frame extraction: ~2 minutes
- Processing (Floyd-Steinberg): ~45 minutes
- Video encoding: ~3 minutes
- Total: ~50 minutes
- GPU acceleration via wgpu
- Additional color palettes (Gameboy, NES, C64)
- Pure Rust video encoding (rav1e integration)
- Multithreaded batch processing
- Web interface: video upload support
- Web interface: palette editor
- Advanced filters (CRT scan lines, bloom)
- Python bindings via PyO3
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
# Run tests
cargo test
# Run benchmarks
cargo bench
# Format code
cargo fmt
# Lint
cargo clippy
# Build docs
cargo doc --no-deps --openThis project is licensed under the MIT License - see the LICENSE file for details.
- Developed with Claude Code and OpenAI Codex
- Pico8 palette by Lexaloffle
- Dithering algorithms based on classic computer graphics techniques
- Third-party video processing reference: video-to-pixel-art
- Big Buck Bunny test video © Blender Foundation | creative commons
For questions, issues, or feature requests, please open an issue.