Image dithering/filter tool that applies effects to a portion of an image using Bitcoin Austria brand red (#E3000F) and other art styles.
- Multiple dithering regions: Rectangles, circles, or traditional cut modes
- Combine shapes: Mix multiple rectangles and circles in a single image
- Flexible positioning: All coordinates accept any float value (not limited to 0-1)
- Randomized dithering: Floyd-Steinberg with threshold randomization for organic, less regular patterns
- Bitcoin Austria brand red: Uses #E3000F for dithered pixels
- Background modes: Choose white (default) or dark (#222222) background for dithered areas
- Grayscale mode: Convert entire image to grayscale before applying dithering effects
- Density control: Uniform fade or angle-based gradient transitions
- Gradient effects: Create smooth density transitions across the image with any angle (0-360°)
- Format preservation: Maintains original image format (JPEG/PNG)
- Smart naming: Automatic output naming with collision avoidance
- Zero-configuration: Ready to run out of the box with UV
- Shaded Dithering: Brand dots are shaded proportionally to the underlying image darkness
./effect.sh --pos=0.67 --grayscale test-image-1920px.jpgThe entire image is converted to grayscale, then the right 33% is dithered in Austrian flag red (#E3000F).
./effect.sh --rect=0,0,0.2,1 --rect=0.8,0,1,1 --circle=0.5,0.5,0.2 --jitter=33 --grayscale test-image-1920px.jpgCreates two wider vertical strips on the edges (20% width each) plus a circle in the center, all on a grayscale background. The jitter value of 33 adds subtle randomness to the dithering pattern.
./effect.sh --pos=0.5 --fade=0.4 --background=dark --grayscale test-image-1920px.jpgDithers the right 50% of the image with 40% pixel density on a dark background (#222222), creating a subtle, translucent effect.
Note: Very low fade values (below 0.2) can break the error diffusion algorithm and produce random noise instead of coherent dithering.
./effect.sh --circle=0.5,0.5,0.35 --jitter=100 --grayscale test-image-1920px.jpgHigh jitter (100) creates more randomness and organic texture. The default jitter is 30, and --no-randomize disables it completely for classic Floyd-Steinberg dithering.
./effect.sh --circle=0.5,0.5,0.3 --satoshi-mode --grayscale examples/test-image-1920px.jpg“The network self-adjusts.”
Dynamic thresholding adapts to local brightness, preserving more detail in bright and dark areas.
./effect.sh --glitch=0.02 --grayscale test-image-1920px.jpg"Corrupt the Dither."
Introduces controlled data degradation for cyberpunk aesthetics:
- Swaps random rows
- Shifts color channels (RGB offset)
- Repeats error diffusion passes with feedback noise
./effect.sh --glitch=0.1 --grayscale --fade=0.9 --jitter=10 test-image-1920px.jpgHigher glitch intensity (0.1) combined with heavy 90% fade and low jitter creates an extremely sparse, ghostly corruption effect. The minimal pixel distribution lets the glitching breathe.
./effect.sh --circle=0.25,0.5,0.2 --rect=0.5,0,1,1 --gradient=0,1.0,0.1 --grayscale test-image-1920px.jpgGradient density transitions create smooth fade effects. This example combines a circle on the left with the right half dithered, using a horizontal gradient that goes from 100% density (left) to 10% density (right). The image gradually fades from solid dithering to sparse, creating a natural transition effect.
The --gradient option uses angle,start,end format:
- 0°: Horizontal left→right
- 90°: Vertical top→bottom
- 180°: Horizontal right→left
- 270°: Vertical bottom→top
Use any angle for diagonal or custom directions.
./effect.sh --circle=0.25,0.5,0.2 --rect=0.5,0,1,1 --gradient=180,1.0,0.1 --grayscale test-image-1920px.jpgSame composition as Example 8, but with the gradient reversed (180°). The gradient now goes from 100% density (right) to 10% density (left), creating the opposite fade direction. This demonstrates how changing the angle parameter reverses the gradient flow while keeping all other parameters identical.
./effect.sh --shade="1,q=4" --grayscale test-image-1920px.jpgBy default, dither dots are shaded proportionally to the darkness of the original image (darker area = darker dot). This creates a "many shaded reds" effect instead of solid flat color.
The --shade parameter allows customization:
factor: Float value (default 1).0restores the old solid-color behavior.q=N: Quantize the shading into N discrete levels.
In this example, q=4 restricts the red dots to just 4 distinct shades of intensity.
./effect.sh --shade="1,q=4" --glitch=0.02 --grayscale test-image-1920px.jpgCombines shaded dithering with 4-level quantization and a 10% glitch effect for a corrupt, lo-fi aesthetic.
See ORIGINAL.md for the full guide on --mode=original, including annotated source code, CLI options, and example images.
This project uses UV for dependency management. If you don't have UV installed:
curl -LsSf https://astral.sh/uv/install.sh | shThen install dependencies:
uv sync# Default: vertical cut at golden ratio (~38%/~62%)
./effect.sh image.webp
# Works with PNG too
./effect.sh image.pngLaunch the local Web UI to experiment with a visual dashboard that supports zoomable previews and drag-and-drop file uploading.
# Start Web UI on localhost:8050
uv run python webui.pyDefine rectangular regions for dithering. Coordinates are specified as x1,y1,x2,y2 where (x1,y1) is the top-left corner and (x2,y2) is the bottom-right corner.
# Single rectangle: dither the right half
./effect.sh --rect=0.5,0,1,1 image.webp
# Two vertical strips on the edges
./effect.sh --rect=0,0,0.1,1 --rect=0.9,0,1,1 image.webp
# Top and bottom strips
./effect.sh --rect=0,0,1,0.1 --rect=0,0.9,1,1 image.webpDefine circular regions for dithering. Coordinates are specified as x,y,radius where (x,y) is the center.
# Circle in the center
./effect.sh --circle=0.5,0.5,0.3 image.webp
# Multiple circles
./effect.sh --circle=0.25,0.25,0.15 --circle=0.75,0.75,0.15 image.webp# Top strip plus center circle
./effect.sh --rect=0,0,1,0.1 --circle=0.5,0.5,0.2 image.webp
# Frame effect: strips on all edges plus center circle
./effect.sh --rect=0,0,0.05,1 --rect=0.95,0,1,1 --rect=0,0,1,0.05 --rect=0,0.95,1,1 --circle=0.5,0.5,0.25 image.webp# Vertical cut (default): left part original, right part dithered
./effect.sh --cut=vertical image.webp
# Horizontal cut: top part original, bottom part dithered
./effect.sh --cut=horizontal image.webp
# Custom position (0.0 to 1.0)
# For vertical: --pos=0.4 means left 40% original, right 60% dithered
./effect.sh --cut=vertical --pos=0.4 image.webp
# For horizontal: --pos=0.4 means top 40% original, bottom 60% dithered
./effect.sh --cut=horizontal --pos=0.4 image.webpChoose different dithering algorithms with --pattern.
# Ordered Bayer matrix
./effect.sh --pattern=ordered image.webp
# Vintage Atkinson style
./effect.sh --pattern=atkinson image.webp
# Hal Finney tribute (scanlines + noise)
./effect.sh --pattern=hal --grayscale image.webp
# Blue noise (least visible structure, preserves detail)
./effect.sh --pattern=blue-noise --grayscale image.webpChoose different color palettes with --brand to match your project's identity.
# Bitcoin Austria red (default)
./effect.sh --brand=btcat image.webp
# Lightning Network orange
./effect.sh --brand=lightning --circle=0.5,0.5,0.3 image.webp
# Cypherpunk/Matrix green
./effect.sh --brand=cypherpunk --grayscale image.webp
# RGB mode: separate dithering for R, G, B channels
./effect.sh --brand=rgb --pattern=ordered image.webp
# Combine branding with patterns and shapes
./effect.sh --brand=lightning --pattern=bitcoin --rect=0.5,0,1,1 image.webpRGB Mode is special: instead of using a single color, it dithers each color channel (Red, Green, Blue) independently, creating unique color-separation effects. This works with all dithering patterns.
Converts the entire image to grayscale before applying dithering effects.
# Grayscale entire image, then dither
./effect.sh --grayscale image.webp
# Combine with shapes
./effect.sh --grayscale --circle=0.5,0.5,0.3 image.webpControl the density of dithering across all dithered areas.
# Sparse dithering: only 10% of pixels dithered
./effect.sh --fade=0.1 image.webp
# 50% density for subtle effect
./effect.sh --fade=0.5 --rect=0.5,0,1,1 image.webp
# Combine with grayscale
./effect.sh --grayscale --fade=0.3 --circle=0.5,0.5,0.4 image.webpCreate smooth density transitions across the image using angle-based gradients.
Format: --gradient=angle,start,end
- Angle: 0-360° (0=left→right, 90=top→bottom, 180=right→left, 270=bottom→top)
- Start: Density at gradient start (0.0 to 1.0)
- End: Density at gradient end (0.0 to 1.0)
# Horizontal gradient: sparse on left, dense on right
./effect.sh --gradient=0,0.1,1.0 image.webp
# Vertical gradient: fade from top to bottom
./effect.sh --gradient=90,0.0,1.0 image.webp
# Reverse horizontal: dense on left, sparse on right
./effect.sh --gradient=180,1.0,0.2 image.webp
# Diagonal gradient (45 degrees)
./effect.sh --gradient=45,0.0,1.0 image.webp
# Fade to white: image gradually vanishes
./effect.sh --gradient=0,1.0,0.0 image.webp
# Combine with shapes: gradient only in specific areas
./effect.sh --gradient=0,1.0,0.1 --circle=0.25,0.5,0.2 --rect=0.5,0,1,1 image.webpNote: Gradient overrides --fade if both are specified. The gradient is computed based on pixel position in the image and applied only to dithered regions.
Choose between white or dark background for dithered areas.
# White background (default): red dots on white
./effect.sh --background=white image.webp
# Dark background: red dots on dark gray (#222222)
./effect.sh --background=dark image.webp
# Combine with fade for subtle dark effect
./effect.sh --background=dark --fade=0.4 --pos=0.5 image.webp# Adjust the dithering threshold (0-255, default: 128)
# Lower values = more red pixels, Higher values = more white pixels
./effect.sh --threshold 100 image.webp# Randomization is enabled by default. Control the amount with --jitter (default: 30.0)
./effect.sh --jitter 50 image.webp
# Disable randomization for classic Floyd-Steinberg
./effect.sh --no-randomize image.webpMake the dither pattern proportional to the image width.
# Set a reference width (default: 1024).
# If input is wider than this, dither points become larger.
./effect.sh --reference-width 800 image.webpAdjust the darkness of the dithered output.
# Make the result darker (fewer white pixels)
./effect.sh --darkness 30 image.webp
# Make the result lighter (more white pixels)
./effect.sh --darkness -30 image.webpEnable dynamic thresholding that adapts per pixel based on local brightness.
# Brighter areas get higher threshold -> fewer red pixels
./effect.sh --satoshi-mode image.webpIntroduce controlled digital corruption.
# Mild glitch (0.1): Subtle artifacts, slight color shift
./effect.sh --glitch=0.02 image.webp
# Heavy glitch (0.5+): Strong channel shifting, row swapping, and noise
./effect.sh --glitch=0.5 image.webpControl how the brand dots are shaded based on the underlying grayscale image.
# Default behavior (factor=1): dots are darker in dark areas
./effect.sh image.webp
# Disable shading (restore old solid-color dots)
./effect.sh --shade=0 image.webp
# Half strength shading
./effect.sh --shade=0.5 image.webp
# Quantized shading (restrict to 3 levels)
./effect.sh --shade="1,q=3" image.webp# Grayscale + sparse dithering + custom shapes
./effect.sh --grayscale --fade=0.2 --rect=0,0,0.2,1 --circle=0.6,0.5,0.3 image.webp
# High jitter + darkness + circle
./effect.sh --jitter=100 --darkness=50 --circle=0.5,0.5,0.4 image.webp
# Multiple rectangles with threshold adjustment
./effect.sh --threshold=100 --rect=0,0,0.3,1 --rect=0.7,0,1,1 image.webpThe default output format is WebP, which offers excellent quality at smaller file sizes and is supported by all modern browsers. The tool generates output files with the format:
[original-name]-dither.webp- If the file exists, it appends a number:
[original-name]-dither-1.webp - When using
--output, the format is determined by the file extension (.webp,.jpg,.png)
The recommended input width for best results is 1920px, which covers standard desktop displays and looks sharp on tablets at 2x.
- Load and prepare: Reads the input image and converts to RGB if needed
- Grayscale conversion (optional): If
--grayscaleis specified, converts the entire image to grayscale - Create dithering masks: Generates boolean masks for rectangles and circles (combined with logical OR)
- Dithering: Applies Floyd-Steinberg error diffusion dithering with optional randomization
- Density control (optional):
- If
--gradientis specified, creates angle-based gradient density mask across entire image - If
--fadeis specified (and no gradient), creates uniform density mask - Density mask probabilistically skips pixels for sparse/fade effects
- If
- Colorization: Renders dithered pixels in Bitcoin Austria red (#E3000F) on chosen background (white or dark #222222)
- Compositing: Applies dithered regions to the base image using the masks
- Save: Outputs the result in the same format as the input with smart naming
- Python 3.10+
- Pillow (for image processing)
- NumPy (for array operations)
- Click (for CLI with automatic validation)
All dependencies are managed automatically by UV.
Apache License 2.0
Copyright 2026 Harald Schilly hsy@bitcoin-austria.at
Licensed under the Apache License, Version 2.0. See the LICENSE file for details.

























