╔═╗╦ ╦ ╦╔═╗
║ ╦║ ╚╦╝╠╣
╚═╝╩═╝ ╩ ╚
High-performance ASCII video engine
Glyf is a terminal-based media engine written in Go that renders video as synchronized ASCII art in the terminal while playing the accompanying audio track. It decodes raw video frames, converts each frame into a grid of characters based on pixel luminance, and paces frame output against a wall-clock timer to keep video and audio in sync.
- Real-time video-to-ASCII rendering based on pixel luminance mapping
- Synchronized audio playback alongside ASCII video output
- Frame pacing engine that keeps rendering aligned to a target frame rate
- Graceful terminal cleanup on interrupt (cursor restoration on
Ctrl+C) - Minimal dependencies — relies on
ffmpeg/ffplayfor decoding and playback
Glyf is organized into four independent packages coordinated by a central engine:
| Package | Responsibility |
|---|---|
video |
Spawns an ffmpeg process to decode the input file into raw RGB24 frames and exposes them as image.Image values |
render |
Converts a decoded frame into an ASCII string using a luminance-to-character gradient |
audio |
Spawns ffplay in the background to handle audio playback independently of the render loop |
engine |
Owns the main playback loop: pulls frames from the decoder, renders them, and paces output against a synchronized clock |
The playback loop and the audio process run concurrently but independently — audio playback is started once via ffplay, while the video loop paces itself against an internal clock (SyncEngine) seeded at playback start, so frame timing does not drift from the decode rate.
- Go 1.26.2 or later
- FFmpeg (
ffmpegandffplaybinaries) available on the systemPATH - A terminal emulator with ANSI escape sequence support
Clone the repository and build the binary:
git clone https://github.com/SupriyoP09/Glyf.git
cd Glyf
go build -o glyf ./cmd/glyfRun the binary with a path to a video file:
./glyf -file path/to/video.mp4If no -file flag is provided, Glyf defaults to looking for test.mp4 in the current directory.
Press Ctrl+C at any time to stop playback; the terminal cursor is restored automatically on exit.
- Decoding —
video.NewDecoderlaunchesffmpeg, instructing it to output raw RGB24 frames at a fixed resolution over a pipe. Each call toNextFramereads exactly one frame's worth of bytes and reconstructs it as animage.Image. - Rendering —
render.ImageToASCIIwalks every pixel, computes luminance using the standard weighted RGB formula, and maps the result onto a fixed ASCII character ramp (from lightest to darkest character). - Audio —
audio.Playstartsffplayin headless mode (-nodisp) so audio decoding and output run independently of the render loop. - Synchronization —
engine.SyncEnginerecords a start timestamp and, for each frame index, sleeps just long enough to keep actual elapsed time aligned with the expected frame time for a fixed frame rate. - Output — Each rendered frame is written to the terminal using ANSI cursor-repositioning sequences, avoiding a full screen clear per frame to reduce flicker.
Glyf/
├── cmd/
│ └── glyf/
│ └── main.go # CLI entry point
├── engine/
│ └── sync.go # Playback loop and frame-rate synchronization
├── audio/
│ └── player.go # Audio playback via ffplay
├── render/
│ └── ascii.go # Luminance-to-ASCII conversion
├── video/
│ └── decoder.go # ffmpeg-based frame decoding
└── go.mod