Open-source project
YusufB5/ASCILINE avatar
YusufB5/ASCILINE

ASCILINE: ASCII video streaming to an HTML5 canvas over WebSocket

A high-performance ASCII video rendering engine featuring real-time WebSocket binary streaming and an isolated compiler for serverless static generation. Built for low-latency 30 FPS playback on HTML5 Canvas.

2,734 stars312 forksJavaScriptNOASSERTION

At a glance

What is it?
ASCILINE renders video to ASCII or colored blocks in Python and streams binary frames to a vanilla JS canvas player. It is aimed at low-bandwidth, zero-GPU playback, and it is still an alpha with a split AGPL/MIT licence.
Who is it for?
Adopt ASCILINE if you need text-based video playback on a device or network where H.264 or VP9 decoding is not an option, and if you can live with an alpha that has shipped only patch releases. Do not adopt it if you need a stable, versioned API for a production player, or if you cannot accept the AGPL v3 terms on the Python engine, since the MIT licence covers only the JavaScript SDK.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 17, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What ASCILINE solves, and for whom

Standard video delivery assumes the client can decode H.264 or VP9. That assumption fails on microcontrollers, smart appliances, retro terminals and weak devices without a hardware decoder, and it also fails on constrained networks where a video bitrate is simply too much. ASCILINE moves the decoding work to a Python server, converts each frame into characters or colored blocks, and sends the result to the browser as binary frames over a WebSocket. The README frames the goal as "Zero GPU, ultra-low bandwidth (ASCII modes)", noting that fewer columns means proportionally less bandwidth.

The second consequence is that the output is text. To the browser there is no video element, no codec negotiation and no autoplay restriction; there is a canvas being painted with characters. The README's first design goal is that this makes the stream "raw HTML/Canvas text", so CSS filters such as glows, shadows and animations can be applied to what would otherwise be a video surface. If you want a typographic display rather than a faithful reproduction, that is the point. If you want fidelity, pixel mode is the closer approximation and the README describes it as approaching 360p quality.

How the pipeline works, from OpenCV to the canvas

The architecture has three parts, and the repository layout reflects them directly. The backend is Python and FastAPI: ascii_video_player2.py holds the VideoDecoder and AsciiMapper, decoding via OpenCV and mapping pixels to characters with NumPy. The frontend is vanilla JavaScript: app.js opens the WebSocket, manages a jitter buffer, and runs the canvas render loop. Between them sits codec.py, described as the master Python encoder, with codec.js as the root JavaScript decoder tuned for live WebSocket streaming.

Communication starts with a custom INIT handshake that negotiates resolution and frame rate, after which binary frames flow. The README states that frames are streamed as raw Uint8Array straight to the canvas, and that a master clock uses the audio track as the absolute time reference to keep A/V synchronized. The Adaptive Frame Codec is opt-in and applies to ASCII modes 2 through 6, with codec.py listing RAW, ZLIB, DELTA, RLE and DCT as the available encodings. Playback is tuned for 24 to 30 FPS, and the README says higher-FPS sources are automatically decimated for stability, so a 60 FPS input does not become a 60 FPS stream.

The codec choice is the main tuning lever and it is not free. RAW is the simplest path and the most bandwidth-hungry; DELTA and RLE reduce payload size at the cost of encoder work and state. The documentation does not publish measured bandwidth or latency figures for each mode, so the only honest way to choose is to run your own content through the modes and watch the frame timing in the browser. Note also that the codec is opt-in and limited to ASCII modes 2 to 6, so pixel mode and the lower ASCII modes do not benefit from it.

Installing ASCILINE and streaming a first video

The requirements are Python 3.9 or newer, and pyproject.toml declares fastapi, uvicorn, opencv-python, numpy and websockets as dependencies. The requirements file carries the same five and notes that installing the package in editable mode is preferred. From the repository root:

bash
pip install -e .

With the dependencies in place, start the streaming server. stream_server.py is the entry point listed in pyproject.toml, and the Docker compose file maps the service to port 8000, so that is the port to open.

bash
python stream_server.py

The README does not spell out the exact server flags, so treat the command above as the starting point and check the file for its argument parser before scripting anything. Once the server is running, open the web client in a browser and the INIT handshake negotiates resolution and frame rate before the first binary frame arrives.

Docker is the shorter path if you would rather not manage the Python environment. The compose file mounts ./videos into the container at /app/videos, so local .mp4 and .mkv files placed there become available to the player.

bash
docker compose up

The compose file also sets PYTHONUNBUFFERED=1 and keeps stdin and a TTY attached for /help commands, with a comment noting that the high-FPS yes/no prompt is skipped automatically in Docker. For a first real test, put one short clip in videos/ and play it in ASCII mode before experimenting with pixel mode or the adaptive codec, because those two change the payload characteristics you are trying to evaluate.

Where ASCILINE is the wrong tool

The project is published as an alpha. The three most recent releases, v0.1.1 through v0.1.3, are all patch-level and span early September 2026, and the release titles describe fixes rather than new surface area: a codec fix, cross-origin audio and SDK improvements, and audio/video sync state isolation. A player built on this should expect the wire format and the SDK to move. There is no documented API stability commitment, and the README does not describe a rollback or migration procedure for the codec or the handshake.

Fidelity is the second boundary. ASCII output is a deliberate loss of information. If your content depends on faces, small text, fine gradients or colour accuracy, the character grid will not carry it, and pixel mode only reaches what the README calls approaching 360p. The server also does the encoding work, so the CPU cost moves to the host rather than disappearing; the README's claim is about removing the GPU requirement on the client, not about removing compute.

Finally, the licence split matters for how you can use it. The Python engine is AGPL v3, per the badge and pyproject.toml, while the JavaScript SDK is MIT, with separate LICENSE-AGPL and LICENSE-MIT files in the repository. The README does not explain which side of the boundary a given deployment falls on, and this article cannot give legal advice, so read both files and decide with your own counsel before shipping anything that links the engine into a network service.

ASCILINE compared with Libcaca

Libcaca is the obvious reference point for anyone searching for an ASCII video renderer, and the approaches differ in a way that matters. Libcaca is a C library that runs on the client: you link it, and it converts and draws locally, with its own colour and dithering model. ASCILINE inverts that. The conversion happens on a Python server, and the client receives pre-rendered frames over a WebSocket and paints them onto an HTML5 canvas. That means the client needs a browser and a network connection, not a compiled binary, and it means the server carries the encoding load for every viewer.

The trade-off is straightforward. ASCILINE can serve a weak device that could never run the conversion itself, and it can change the rendering mode without touching the client, since mode and volume are set per video in the JSON playlist. Libcaca can run offline, has no network dependency and no per-viewer server cost. If your target is a terminal application on a machine you control, Libcaca is the closer fit. If your target is a browser on a device with no GPU and a thin connection, ASCILINE's server-side model is the one that makes the playback possible at all.

Maintenance, packaging and the two licences

The repository is not archived and the last push was on 2026-09-15, two days before this writing, so the code is being touched. That is not the same as stable. The version numbers tell the real story: 0.1.3 in package.json, 0.1.3 in pyproject.toml, and release titles that all carry an alpha suffix. Treat every upgrade as a potential breaking change and pin the version you deploy.

The packaging is split along the same line as the licence. The Python side installs as the asciline package with py-modules listing ascii_video_player2, codec, compiler, logo, stream_server and ytdl, and there is an optional ytdlp extra that pulls in yt-dlp for fetching remote video. The JavaScript side publishes as asciline-player with src/asciline-player.js as both main and module, and its test script runs node test/test_sdk_import.js. If you only consume the player in a browser, you are working against the MIT-licensed SDK; if you run the engine, AGPL v3 applies to it. Upgrading means checking both package manifests, since they version independently even when the numbers currently match.

Editorial conclusion

Adopt ASCILINE if you need text-based video playback on a device or network where H.264 or VP9 decoding is not an option, and if you can live with an alpha that has shipped only patch releases. Do not adopt it if you need a stable, versioned API for a production player, or if you cannot accept the AGPL v3 terms on the Python engine, since the MIT licence covers only the JavaScript SDK. Before committing, verify three things yourself: whether the mode of the video you actually want to play survives the codec path, whether the Docker image on port 8000 keeps audio and video in sync in your browser, and which licence file applies to the code you plan to redistribute.

Frequently asked questions

What is ASCILINE?

ASCILINE is a cross-platform real-time ASCII video rendering engine. It converts video frames to characters or colored blocks on a Python server and streams them as binary frames over a WebSocket to a vanilla JavaScript HTML5 canvas player.

How do I install and run ASCILINE?

Install the Python dependencies with pip install -e . from the repository root, then start the server with python stream_server.py. Alternatively, docker compose up builds the image and exposes it on port 8000, with ./videos mounted at /app/videos inside the container.

Does ASCILINE need a GPU to play video in the browser?

The README states that the ASCII modes are designed for zero-GPU devices, because the server does the conversion and the browser only paints text onto a canvas. There is no video element and no browser-side codec decoding, so no hardware decoder is required on the client.

What licence does ASCILINE use?

The Python engine is licensed under AGPL v3, as shown by the badge and pyproject.toml, while the JavaScript SDK is MIT. The repository carries separate LICENSE-AGPL and LICENSE-MIT files.

Official sources

  1. Issues
  2. README
  3. Releases
  4. YusufB5/ASCILINE on GitHub
Community notes

Community notes