Convert images and videos to ASCII art using shape-vector 6D matching, based on the technique described in ASCII Rendering by Alex Harri.
The algorithm divides each image into a grid of cells. For each cell, it computes a 6-dimensional "shape vector" by measuring average brightness within 6 circular sampling regions arranged in a 2x3 pattern (2 columns, 3 rows, vertically staggered for better coverage).
Every printable ASCII character (32-126) is pre-rendered to a bitmap and sampled identically, producing a lookup table of 6D vectors. For each image cell, the algorithm finds the character whose vector has the minimum Euclidean distance to the cell's vector. The average RGB color of the cell is preserved, enabling full-color output.
- Character Atlas -- Render all printable ASCII characters at a fixed font size. Compute 6D shape vectors. Normalize and apply a contrast exponent to sharpen distinctions.
- Image Sampling -- Divide the input image into a grid. For each cell, compute the 6D shape vector from the grayscale channel and the average RGB from the color channels.
- Matching -- Vectorized nearest-neighbor search in 6D space using expanded Euclidean distance.
- Output -- Emit as PNG/JPG image, ANSI truecolor terminal text, MP4/GIF video, or JSON.
Requires Python 3.11+ and uv.
git clone git@github.com:robotrocketscience/ascii-render-machine.git
cd ascii-render-machine
uv syncVideo output requires ffmpeg installed on your system.
# Render an image to a PNG
uv run ascii-render-machine photo examples/sample.jpg --cols 120 --output result.png
# Render an image to the terminal
uv run ascii-render-machine photo examples/sample.jpg --cols 80 --terminal
# Convert video to MP4
uv run ascii-render-machine video input.mp4 --cols 120 --fps 10 --output result.mp4
# Convert video to GIF
uv run ascii-render-machine video input.mp4 --cols 60 --fps 8 --output result.gif
# Play video as ASCII in the terminal
uv run ascii-render-machine video input.mp4 --cols 80 --terminal --fps 10ascii-render-machine photo INPUT [OPTIONS]
| Option | Default | Description |
|---|---|---|
--cols N |
80 | Number of character columns |
--rows N |
auto | Number of character rows (auto-calculated from aspect ratio if omitted) |
--terminal |
off | Render to terminal with ANSI truecolor |
--no-color |
off | Disable color in terminal output |
--output PATH |
none | Output file (.png, .jpg, or .json) |
--font-size N |
16 | Font size used for the character atlas and image rendering |
--contrast F |
1.2 | Contrast exponent for shape vector enhancement |
ascii-render-machine video INPUT [OPTIONS]
| Option | Default | Description |
|---|---|---|
--cols N |
80 | Number of character columns |
--rows N |
auto | Number of character rows |
--fps N |
10 | Target frames per second |
--terminal |
off | Play ASCII video in the terminal |
--output PATH |
none | Output video file (.mp4, .gif, or .webm). Requires ffmpeg. |
--font-size N |
16 | Font size used for the character atlas |
--contrast F |
1.2 | Contrast exponent |
Renders ASCII art as colored monospace characters on a black background. Supports any format Pillow can write (PNG, JPG, BMP, TIFF, etc.).
Each frame is converted to ASCII art, rendered as an image, and piped to ffmpeg for encoding. Supports MP4 (H.264), GIF, and WebM (VP9).
Prints ANSI truecolor (24-bit) text directly to the terminal. For video, clears the screen between frames for real-time playback.
{
"cols": 80,
"rows": 40,
"cells": [
{"char": "@", "r": 200, "g": 150, "b": 50},
...
]
}The project uses pyright in strict mode:
uv run pyrightuv run pytestuv run python examples/generate_sample.py- Pillow -- Image loading, character bitmap rendering, and image output
- opencv-python-headless -- Video frame extraction
- NumPy -- Vectorized shape vector computation and matching
- ffmpeg -- Video encoding (system dependency, required for video output only)
MIT -- see LICENSE.