 Command

Pranesh Nikhar's personal site. Vim-style keybinds for navigation; theme + font pickers below.

Theme
 Font Body Code
Reader
Keybinds
Navigation
j / ↓ Next item k / ↑ Previous item g First item in region G Last item in region zz Center focused item h / l Move left/right region ] / [ Next/previous heading } / { Next/previous block d / u Half-page down/up
Layout
<zh> / <zl> Toggle left/right sidebar <zr> Toggle reader view <zj> / <zk> Focus main/navbar <S-h/j/k/l> Focus left/main/navbar/right ⌃H / ⌃L Focus left/right sidebar ⌃J / ⌃K Focus main/navbar ⇧C / ⇧E Collapse / expand all sections
Dialogs
⌃P / : Command palette ⌃X Theme picker / Search ? Show keybinds Esc / ⌃C Close dialog
History
n Next document b Previous document ⌃O History back ⌃I History forward
 Search
about: Pranesh Nikhar about/more: πŸͺͺ More docs/test: Docs Test ideas: πŸ’‘ Ideas more: βž• More now: Now posts: πŸ“¬ Posts projects: πŸ“š Projects webtui: Style posts/agentic-eda: πŸ“Š AgenticEDA β€” Automated Exploratory Data Analysis with LangGraph posts/cap-theorem-outage-story: 🌐 CAP Theorem with a Real Outage Story posts/codepilot: ✈️ CodePilot β€” From Requirements to Deployable FastAPI Backend posts/common-auth-mistakes: πŸ” Common Auth Mistakes Developers Make posts/compiled-vs-jit-vs-interpreted: ⚑ Why Is X Language Fast or Slow? β€” Compiled vs JIT vs Interpreted posts/cs-degree-gaps: πŸŽ“ Things CS Degrees Don't Teach You posts/cve-2025-breach-analysis: πŸ›‘οΈ CVE-2025 Breach Analysis β€” Midnight Blizzard and the 16 Billion Credential Leak posts/fixloop: πŸ”„ FixLoop β€” AI Agent Loop for Self-Correcting Code posts/functional-vs-oop: ⚑ Functional vs OOP β€” Same Problem, Both Ways posts/getman: 🦾 Getman β€” Declarative API Tester for CLI & TUI posts/how-compilers-optimize: βš™οΈ How Compilers Actually Optimize Your Code posts/http3-quic: ⚑ HTTP/3 and QUIC β€” Why They Matter posts/leetcode-vs-engineering: 🧩 LeetCode vs Real Engineering Skills posts/llm-from-scratch: 🧠 LLM from Scratch β€” GPT-Style Transformer in PyTorch posts/lsm-trees-bloom-filters: 🌳 LSM Trees & Bloom Filters β€” Production Deep Dive posts/mcp-workflow-builder: πŸ”§ MCP Workflow Builder β€” Visual DAG for MCP Tools posts/persistent-memory: 🧠 Persistent Memory β€” Long-Term Memory for AI Agents via MCP posts/playcli: 🎬 PlayCLI β€” Terminal Video Player posts/postgres-mvcc: πŸ—„οΈ How PostgreSQL MVCC Works β€” Multi-Version Concurrency Control Deep Dive posts/raft-consensus: β›΅ Raft Consensus Algorithm Explained posts/rust-borrow-checker: πŸ¦€ Rust Borrow Checker β€” Catches Real Bugs posts/titan: πŸ€– Titan β€” Terminal AI Coding Agent posts/what-happens-url: 🌐 What Happens Between Typing a URL and Seeing the Page posts/what-happens-when-you-run-a-program: βš™οΈ What Actually Happens When You Run a Program posts/zero-knowledge-proofs: πŸ” Zero-Knowledge Proofs Explained Simply webtui/components/accordion: Accordion webtui/components/badge: Badge webtui/components/button: Button webtui/components/checkbox: Checkbox webtui/components/dialog: Dialog webtui/components/input: Input webtui/components/popover: Popover webtui/components/pre: Pre webtui/components/progress: Progress webtui/components/radio: Radio webtui/components/range: Range webtui/components/separator: Separator webtui/components/spinner: Spinner webtui/components/switch: Switch webtui/components/table: Table webtui/components/textarea: Textarea webtui/components/tooltip: Popover webtui/components/typography: Typography webtui/components/view: View webtui/contributing/contributing: Contributing webtui/contributing/contributing: ## Local Development webtui/contributing/contributing: ## Issues webtui/contributing/contributing: ## Pull Requests webtui/contributing/style-guide: Style Guide webtui/contributing/style-guide: ## CSS Units webtui/contributing/style-guide: ## Selectors webtui/contributing/style-guide: ## Documentation webtui/installation/astro: Astro webtui/installation/astro: ## Scoping webtui/installation/astro: ### Frontmatter Imports webtui/installation/astro: ### β€Ήstyleβ€Ί tag webtui/installation/astro: ### Full Library Import webtui/installation/nextjs: Next.js webtui/installation/vite: Vite webtui/plugins/plugin-dev: Developing Plugins webtui/plugins/plugin-dev: ### Style Layers webtui/plugins/plugin-nf: Nerd Font Plugin webtui/plugins/theme-catppuccin: Catppuccin Theme webtui/plugins/theme-custom: Custom Theme webtui/plugins/theme-everforest: Everforest Theme webtui/plugins/theme-gruvbox: Gruvbox Theme webtui/plugins/theme-nord: Nord Theme webtui/plugins/theme-vitesse: Vitesse Theme webtui/start/ascii-boxes: ASCII Boxes webtui/start/changelog: Changelog webtui/start/installation: Installation webtui/start/installation: ## Installation webtui/start/installation: ## Using CSS webtui/start/installation: ## Using ESM webtui/start/installation: ## Using a CDN webtui/start/installation: ## Full Library Import webtui/start/installation: ### CSS webtui/start/installation: ### ESM webtui/start/installation: ### CDN webtui/start/intro: Introduction webtui/start/intro: ## Features webtui/start/plugins: Plugins webtui/start/plugins: ## Official Plugins webtui/start/plugins: ### Themes webtui/start/plugins: ## Community Plugins webtui/start/theming: Theming webtui/start/theming: ## CSS Variables webtui/start/theming: ### Font Styles webtui/start/theming: ### Colors webtui/start/theming: ### Light & Dark webtui/start/theming: ## Theme Plugins webtui/start/theming: ### Using Multiple Theme Accents webtui/start/tuis-vs-guis: TUIs vs GUIs webtui/start/tuis-vs-guis: ## Monospace Fonts webtui/start/tuis-vs-guis: ## Character Cells
 Theme Current: Light j/k or ↑/↓ + Enter

🎬 PlayCLI β€” Terminal Video Player

Real-time video playback in your terminal using ANSI true-color escape sequences, with 5 render modes, audio sync, subtitles, and adaptive quality.

🎯 What It Does

PlayCLI is a real-time terminal video player. It renders videos frame-by-frame directly in your terminal using 24-bit true color ANSI escape sequences β€” no GUI, no browser, just your shell.

$ playcli video.mp4

It supports 5 render modes, audio sync via your system’s audio device, keyboard controls, subtitles, streaming from URLs, static images, playlists, screenshots (PNG + ANSI HTML), and adaptive quality throttling.


🧱 Tech Stack

ComponentTechnology
LanguagePython 3.9+
Video I/Oimageio-ffmpeg (FFmpeg via subprocess)
MathNumPy (vectorized pixel ops)
Audiosounddevice + soundfile (optional)
ImagesPillow (subtitle rendering, static images)
FontsPillow + fontconfig (system font discovery)

Zero external runtime dependencies beyond NumPy and imageio-ffmpeg for the core player. Audio and image extras are optional.


πŸ—οΈ Architecture

ascii_play/
β”œβ”€β”€ cli.py          # Entry point, argparse dispatch
β”œβ”€β”€ player.py       # FFmpeg decode loop, audio sync, keyboard input
β”œβ”€β”€ renderers.py    # 5 render modes (MODES dict)
β”œβ”€β”€ resize.py       # Quality levels: nearest, 4-tap, box filter
β”œβ”€β”€ ansi.py         # ANSI escape sequence helpers
β”œβ”€β”€ subtitle.py     # SRT parser + Pillow text overlay

The data flow:

  1. cli.py parses arguments and dispatches to the player
  2. player.py opens the video via imageio-ffmpeg, reads frames as NumPy arrays, synchronizes to audio via sounddevice (or wall clock if no audio), captures keyboard events via tty.setraw(), and pushes rendered frames to stdout
  3. renderers.py converts each NumPy frame into ANSI escape codes using one of 5 modes
  4. resize.py downsizes frames to fit terminal dimensions at configurable quality
  5. ansi.py provides the low-level ANSI sequences (cursor positioning, RGB color codes, clear screen)
  6. subtitle.py parses SRT files and overlays text via Pillow before rendering

All pixel operations are NumPy-vectorized β€” no Python-level loops over pixels.


🎨 Render Modes

ModeFlagDescription
Half-block--mode block (default)Uses the β–€ character with both foreground and background ANSI colors to double vertical resolution
ASCII art--mode asciiMaps luminance to ASCII characters (@%#*+=-:. )
Braille--mode brailleUses braille dot patterns for high-density dithering
Dither--mode ditherFloyd-Steinberg error diffusion dithering to 2-color
Edge--mode edgeSobel edge detection rendered as ASCII

Half-block mode (β–€) is the star of the show. Each terminal cell renders one β–€ character with a foreground color (top half) and background color (bottom half), effectively doubling the vertical resolution compared to a single colored character. This gives near-photographic quality in modern terminals that support 24-bit color.

πŸ”Š Audio Sync

PlayCLI uses sounddevice to play audio in a separate thread while the main thread renders video frames. The audio thread tracks the current playback position, and the video loop drops or repeats frames to stay in sync. If sounddevice is not installed, it falls back to wall-clock timing.

⌨️ Keyboard Controls

KeyAction
SpacePlay / Pause
q / EscQuit
β†’ / lForward 5s
← / hBackward 5s
↑ / kVolume up
↓ / jVolume down
mMute
fToggle fullscreen (term)
sScreenshot (PNG + ANSI HTML)
0-9Toggle render mode

πŸ“¦ Key Features

  • Adaptive quality: Automatically reduces quality when frame decode falls behind real-time
  • Streaming: URL playback via yt-dlp integration
  • Playlists: JSON playlist format with shuffle
  • Subtitles: SRT file rendering via Pillow with configurable font
  • Screenshots: Captures both a PNG image and an ANSI-escape HTML file
  • Static images: Display images directly (no video)
  • Persistent config: ~/.config/playcli/config.json

πŸš€ Quick Start

pip install playcli
playcli video.mp4

# Stream from YouTube
playcli https://youtu.be/dQw4w9WgXcQ

# Use ASCII mode
playcli video.mp4 --mode ascii

# Keyboard shortcuts shown at bottom of terminal

πŸ’‘ Why It’s Interesting

PlayCLI is a showcase of what’s possible with ANSI terminal escape sequences when you push them to their limit. The half-block rendering technique gives surprisingly good video quality using nothing more than stdout output. It’s genuinely useful for quick video previews without leaving the terminal, watching tutorials in a split pane, or just impressing your friends at a meetup.

 praneshnikhar.site / posts / playcli Β· Top 1:1