cascii 0.18.0

High-performance ASCII art generator for images and videos
Documentation

cascii - Interactive ASCII Frame Generator

cascii is a high-performance, interactive tool for converting videos and image sequences into ASCII art frames.

New: cascii can now be used as both a CLI tool and a Rust library!

When converting a video, the output files will be placed in a directory named after the video file. For example, cascii my_video.mp4 will create a my_video directory.

I recommend installing cascii-viewer to easily play any ascii animation generated or decorator

Features

  • Interactive Mode: If you don't provide arguments, cascii will prompt you for them.
  • Flexible Input: Works with video files or directories of PNGs.
  • Performance: Uses ffmpeg for fast frame extraction and parallel processing with Rayon for ASCII conversion.
  • Video Segments: Specify start and end times to convert only a portion of a video.
  • Presets: --small and --large flags for quick quality adjustments.
  • Non-interactive Mode: Use --default to run without prompts, using default values.
  • Library Support: Use cascii as a dependency in your own Rust projects.
  • Flexible FFmpeg: Uses system ffmpeg by default, or accepts custom paths for bundled/embedded ffmpeg binaries.

Requirements

  • FFmpeg: Required for video conversion. cascii uses ffmpeg for frame extraction and ffprobe for video metadata.
    • By default, cascii looks for ffmpeg and ffprobe on your system PATH
    • For library usage, you can specify custom paths (useful for bundling ffmpeg with your application)

Installation

As a CLI Tool

An install.sh script is provided to build and install cascii to /usr/local/bin.

# Make sure you are in the cascii directory
./install.sh

You will be prompted for your password as it uses sudo to copy the binary.

Alternatively, install from crates.io (once published):

cargo install cascii

As a Library

Add to your Cargo.toml:

[dependencies]
cascii = "0.1"

CLI Usage

cascii

Interactive

Run cascii without any arguments to be guided through the process:

cascii

It will first ask you to select an input file from the current directory, then prompt for the output directory, and finally for the quality settings.

With Arguments

You can also provide arguments directly:

# Basic usage with a video file
cascii my_video.mp4 --out ./my_frames

# Using presets
cascii my_video.mp4 --out ./my_frames --large

# Non-interactive mode (will fail if input is not provided)
cascii my_video.mp4 --out ./my_frames --default

# Convert a 5-second clip starting at 10 seconds into the video
cascii my_video.mp4 --start 00:00:10 --end 00:00:15

Options

  • [input]: (Optional) The input video file or directory of images.
  • -o, --out: (Optional) The output directory. Defaults to the current directory.
  • --columns: (Optional) The width of the output ASCII art.
  • --fps: (Optional) The frames per second to extract from a video.
  • --font-ratio: (Optional) The aspect ratio of the font used for rendering.
  • --start: (Optional) The start time for video conversion (e.g., 00:01:23.456 or 83.456).
  • --end: (Optional) The end time for video conversion.
  • --preprocess: (Optional) ffmpeg -vf filtergraph applied before ASCII conversion (video and single-image inputs).
  • --preprocess-preset: (Optional) Built-in preprocessing preset (use --list-preprocess-presets).
  • --list-preprocess-presets: List built-in preprocessing presets and exit.
  • --default: Skips all prompts and uses default values for any missing arguments.
  • -s, --small: Uses smaller default values for quality settings.
  • -l, --large: Uses larger default values for quality settings.
  • --colors: Generate both .txt and .cframe (color) output files.
  • --color-only: Generate only .cframe files (no .txt).
  • --audio: Extract audio from the video to audio.mp3.
  • --luminance: Luminance threshold (0-255) for what is considered transparent.
  • --keep-images: Keep intermediate PNG frames after conversion.
  • --to-video: Render ASCII frames into a video file (.mp4) instead of frame files. See Export Movie.
  • --video-font-size: Font size in pixels for --to-video rendering (default: 14).
  • --crf: CRF quality for --to-video encoding (0-51, lower = better, default: 18).
  • --trim: Trim equally from all sides of existing frames. Directional overrides: --trim-left, --trim-right, --trim-top, --trim-bottom.
  • --find-loop: Detect repeated frame loops in a directory of frame_*.txt files.
  • -h, --help: Shows the help message.
  • -V, --version: Shows the version information.

Export Movie

cascii can render ASCII art frames into an MP4 video file using --to-video. This works both from a source video (full pipeline) and from a directory of previously generated frames.

From a Video File

Convert a video to an ASCII video in one command:

# White on black (default)
cascii input.mp4 --to-video --default

# Color — each character rendered in its original pixel color
cascii input.mp4 --to-video --colors --default

# Color with audio
cascii input.mp4 --to-video --colors --audio --default

From Existing Frames

If you already have a directory of .cframe or .txt files from a previous cascii run, you can render them to video directly:

# From .cframe files (color) — auto-detected if present
cascii ./my_frames/ --to-video --fps 30 --default

# From .txt files (white on black) — used if no .cframe files exist
cascii ./my_frames/ --to-video --fps 24 --default

# With audio (uses audio.mp3 from the directory if present)
cascii ./my_frames/ --to-video --fps 30 --audio --default

When rendering from a directory, cascii scans for .cframe files first (full color). If none are found, it falls back to .txt files (white on black).

Options

Flag Description Default
--to-video Enable video output mode off
--colors / --color-only Generate color data (needed for color video from a source video) off (white on black)
--video-font-size <PX> Font size in pixels — controls output video resolution 14
--crf <0-51> H.264 quality (lower = better quality, larger file) 18 (visually lossless)
--audio Mux audio into the output video off
--columns <N> ASCII width in characters 400
--fps <N> Frames per second 30

Output resolution is determined by columns × font_size. For example:

Columns Font Size Approximate Width
120 10 ~960px
200 14 ~2800px
200 32 ~6400px
400 14 ~5600px

Examples

# Small compact video
cascii input.mp4 --to-video --colors --default --columns 120 --video-font-size 10

# Medium, readable characters
cascii input.mp4 --to-video --colors --default --columns 200 --video-font-size 14

# Large characters, high resolution
cascii input.mp4 --to-video --colors --default --columns 200 --video-font-size 32

# Lower quality, smaller file size
cascii input.mp4 --to-video --colors --default --crf 28

# Custom output path
cascii input.mp4 --to-video --colors --default -o my_ascii_video.mp4

# Contour-style preprocessing (raw ffmpeg filtergraph)
cascii input.mp4 --default --preprocess "format=gray,edgedetect=mode=colormix:high=0.2:low=0.05,eq=contrast=2.5:brightness=-0.1"

# Built-in preprocessing preset
cascii input.mp4 --default --preprocess-preset contours

# Specific time range with audio
cascii input.mp4 --to-video --colors --audio --default --start 00:00:10 --end 00:00:20

# Render existing frames to video
cascii ./my_frames/ --to-video --fps 30 --default --video-font-size 12

Examples:

Source image:

Source image

Test 1:

settings:

Luminance: 1
Font Ratio: 0.7
Columns: 400

Test 1 output

Test 2:

settings:

Luminance: 35
Font Ratio: 0.7
Columns: 400

Test 2 output

Test 3:

settings:

Luminance: 35
Font Ratio: 0.5
Columns: 400

Test 3 output

Test 4:

settings:

Luminance: 35
Font Ratio: 1
Columns: 400

Test 4 output

Test animation 1:

Reconstituting a few seconds from the clip Aleph 2 by Max Cooper (around 2:30 to 3:00)

Frames: 960
Luminance: 30
Font Ratio: 0.7
Columns: 400
FPS: 30

Demo

Library Usage

cascii can be used as a Rust library in your own projects.

Basic Example - Convert an Image to ASCII

use cascii::{AsciiConverter, ConversionOptions};
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a converter with default configuration
    let converter = AsciiConverter::new();

    // Configure conversion options
    let options = ConversionOptions::default()
        .with_columns(200)
        .with_font_ratio(0.7)
        .with_luminance(20);

    // Convert image to ASCII file
    converter.convert_image(
        Path::new("input.png"),
        Path::new("output.txt"),
        &options
    )?;

    Ok(())
}

Convert Image to String (No File)

use cascii::{AsciiConverter, ConversionOptions};
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let converter = AsciiConverter::new();
    let options = ConversionOptions::default().with_columns(100);
    
    // Get ASCII as a string without writing to file
    let ascii_string = converter.image_to_string(
        Path::new("input.png"),
        &options
    )?;
    
    println!("{}", ascii_string);
    Ok(())
}

Convert a Video to ASCII Frames

use cascii::{AsciiConverter, VideoOptions, ConversionOptions};
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let converter = AsciiConverter::new();

    // Video extraction options
    let video_opts = VideoOptions {
        fps: 30,
        start: Some("0".to_string()),
        end: Some("10".to_string()),  // First 10 seconds
        columns: 400,
        extract_audio: false,
        preprocess_filter: None,
    };

    // ASCII conversion options
    let conv_opts = ConversionOptions::default()
        .with_font_ratio(0.7)
        .with_luminance(20);

    // Convert video
    converter.convert_video(
        Path::new("video.mp4"),
        Path::new("output_frames/"),
        &video_opts,
        &conv_opts,
        false  // Don't keep intermediate PNG files
    )?;

    Ok(())
}

Using Presets

use cascii::AsciiConverter;
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let converter = AsciiConverter::new();

    // Use built-in presets: "default", "small", or "large"
    let small_options = converter.options_from_preset("small")?;

    converter.convert_image(
        Path::new("input.png"),
        Path::new("output.txt"),
        &small_options
    )?;

    Ok(())
}

Custom FFmpeg Path

By default, cascii uses ffmpeg and ffprobe from your system PATH. If you need to use bundled binaries or a custom installation, use FfmpegConfig:

use cascii::{AsciiConverter, FfmpegConfig, VideoOptions, ConversionOptions};
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configure custom ffmpeg paths
    let ffmpeg_config = FfmpegConfig::new()
        .with_ffmpeg("/path/to/ffmpeg")
        .with_ffprobe("/path/to/ffprobe");

    // Create converter with custom ffmpeg paths
    let converter = AsciiConverter::new()
        .with_ffmpeg_config(ffmpeg_config);

    let video_opts = VideoOptions {
        fps: 30,
        start: None,
        end: None,
        columns: 400,
        extract_audio: false,
        preprocess_filter: None,
    };

    let conv_opts = ConversionOptions::default();

    // Video conversion will use the specified ffmpeg binaries
    converter.convert_video(
        Path::new("video.mp4"),
        Path::new("output_frames/"),
        &video_opts,
        &conv_opts,
        false
    )?;

    Ok(())
}

This is useful for:

  • Desktop applications: Bundle ffmpeg with your app for users who don't have it installed
  • Tauri/Electron apps: Use sidecar binaries
  • Docker containers: Use ffmpeg installed in a non-standard location
  • Testing: Use a specific ffmpeg version

API Reference

AsciiConverter

Main converter struct for ASCII art generation.

Methods:

  • new() - Create converter with default configuration
  • with_config(config: AppConfig) - Create with custom configuration
  • with_ffmpeg_config(config: FfmpegConfig) - Set custom ffmpeg/ffprobe paths
  • from_config_file(path: &Path) - Load configuration from file
  • convert_image(input, output, options) - Convert image to ASCII file
  • image_to_string(input, options) - Convert image to ASCII string
  • convert_video(input, output_dir, video_opts, conv_opts, keep_images) - Convert video to ASCII frames
  • convert_video_to_video(input, video_opts, conv_opts, to_video_opts, callback) - Convert video to ASCII video file (.mp4)
  • render_frames_to_video(input_dir, fps, to_video_opts, callback) - Render existing .cframe/.txt frames to video file
  • convert_directory(input_dir, output_dir, options, keep_images) - Convert directory of images
  • get_preset(name) - Get a preset by name
  • options_from_preset(name) - Get conversion options from a preset

FfmpegConfig

Configuration for ffmpeg/ffprobe binary paths.

Methods:

  • new() - Create with default settings (uses system PATH)
  • with_ffmpeg(path) - Set custom ffmpeg binary path
  • with_ffprobe(path) - Set custom ffprobe binary path

ConversionOptions

Options for ASCII conversion.

Fields:

  • columns: Option<u32> - Target width in characters
  • font_ratio: f32 - Font aspect ratio (width/height)
  • luminance: u8 - Luminance threshold (0-255)
  • ascii_chars: String - ASCII character set (darkest to lightest)

Methods:

  • default() - Create with default options
  • with_columns(columns) - Set target width
  • with_font_ratio(ratio) - Set font ratio
  • with_luminance(threshold) - Set luminance threshold
  • with_ascii_chars(chars) - Set custom character set

VideoOptions

Options for video conversion.

Fields:

  • fps: u32 - Frames per second to extract
  • start: Option<String> - Start time (e.g., "00:01:23" or "83")
  • end: Option<String> - End time
  • columns: u32 - Target width in characters
  • extract_audio: bool - Whether to extract audio track from video

ToVideoOptions

Options for rendering ASCII frames to a video file.

Fields:

  • output_path: PathBuf - Output video file path (e.g., "output.mp4")
  • font_size: f32 - Font size in pixels for rendering (default: 14.0)
  • crf: u8 - H.264 quality, 0-51 (default: 18, visually lossless)
  • mux_audio: bool - Whether to mux audio into the output video

Examples

See the examples/ directory for complete examples:

  • simple_image.rs - Basic image conversion
  • simple_video.rs - Video conversion

Run examples with:

cargo run --example simple_image
cargo run --example simple_video

Sample commands

Test Image

./target/release/ascii-gen
--input ./some_frames_dir
--out ../output/sunset_hl
--font-ratio 0.7

Test Video

./target/release/ascii-gen
--input ../input.webm
--out ../output/sunset_hl
--columns 800
--fps 30
--font-ratio 0.7

Acknowledgements

This project is inspired by developedbyed's video that I recommend watching, I reused the logic from his bash script and rewrote it in rust so that it could process faster files with more details.