How asciify-engine compares

The same pictures, the same grid and the same font for every renderer, with photos the engine was not tuned on. On photographs, line art and type asciify-engine is ahead of every renderer tested, close up and at a distance: the two most used free generators, ASCII Magic and Devault (rebuilt from the code their sites ship, with their defaults), aalib, the long-standing quality reference (better on both measures on 16 of 20 photos), ascii.rest, the library Cloudflare sponsors, and three.js. On screen it draws live video with less of the main thread and less memory than the WebGL heroes design studios ship, and in the terminal it beats jp2a and chafa on every picture.

Results

Fidelity is SSIM against the picture after a viewing-distance blur, close and far; higher is closer. Photo scores are the mean over 20 photos the engine was not tuned on.

RendererPhotosLine artType1080p frame (main thread)Flicker (video)Runs in NodeSize (gzip)
asciify-engine on the GPU (createAsciiGpu)0.596 / 0.8840.861 / 0.8800.897 / 0.8890.50 ms3.8 %Browser, worker+6.6 kB
asciify-engine on the GPU, tone0.568 / 0.8800.797 / 0.9500.881 / 0.9400.45 ms3.2 %Browser, worker+6.6 kB
asciify-engine/headless (CPU)0.592 / 0.8790.846 / 0.8760.902 / 0.88944 ms3.8 %Yes4.7 kB
Devault generator (Detailed)0.425 / 0.7860.713 / 0.8710.813 / 0.871147 ms36 %Non/a (an app)
Devault generator (Classic)0.420 / 0.7650.728 / 0.8450.833 / 0.862154 ms6.8 %Non/a (an app)
ASCII Magic (Characters)0.366 / 0.6390.706 / 0.5730.875 / 0.8423.4 ms9.4 %Non/a (an app)
aalib.js0.534 / 0.8680.799 / 0.7980.876 / 0.868146 ms34 %No8.6 kB
ascii.rest (shade)0.418 / 0.7810.686 / 0.8470.786 / 0.846176 ms7 %Yes9.1 kB
three.js AsciiEffect0.446 / 0.7790.718 / 0.6010.874 / 0.836363 ms7.8 %No131 kB with three
Naive ramp (what an agent writes)0.427 / 0.7500.737 / 0.6740.841 / 0.81028 ms4.7 %If written ton/a

The frame time is a 1920 by 1080 frame converted to 160 columns of text, a fresh frame each run as with video, timed on the main thread. On the GPU, createAsciiGpu matches every cell by shape and returns the same grid as the CPU path, with the same scores; the main thread only uploads the frame and reads back one small texture once the GPU is done. It works in a worker of its own, so the page only hands each frame over; error diffusion is settled on the GPU too, so every mode costs about the same. For the GPU converter and ASCII Magic the time is the median of 15 runs from Chrome's own trace of the main thread, garbage collection included: the converter's slowest run (1.3 ms) is half ASCII Magic's fastest (2.6 ms). Flicker is the share of characters that change per frame as the photo pans slowly. GPU renderers that draw pixels rather than text are compared in the full report.

On screen, live

Both generators draw their characters on the page, so what drops frames is converting and drawing a frame on the main thread. Drawing a live 1080p source to the screen, asciify's GPU field (shape sampling, bloom and the fluid hover on) takes 0.9 to 1.5 ms of main thread a frame at 12 px cells and 0.9 ms at 4 px, and about 0.4 to 0.5 ms drawn in a worker. ASCII Magic takes 125 and 299 ms, and Devault 364 and 272 ms for its conversion alone, with its drawing not counted. The field does the work on the GPU, so its cost stays flat however small the cells; drawing characters one by one grows with every cell.

See it

Each picture through six renderers at 100 columns: a naive ramp, three.js AsciiEffect and ascii.rest on top; aalib.js, asciify-engine before this release and asciify-engine/headless below.

Six renderers drawing the same line art in characters
Six renderers drawing the same type in characters
Six renderers drawing the same photograph in characters

Studio heroes

Design studios draw their ASCII heroes with a WebGL shader. 2025.unseen.co's ASCII pass was rebuilt from the shader and settings in its live code (one pixel per 12 px cell, characters from " .,-+=*UNSEEN2025"), and the open-source webgl-ascii-hero was run unchanged, both against asciify-engine's GPU field on the same 20 photos.

GPU rendererPhotosLine artType
asciify-engine field, shape sampling0.618 / 0.9220.852 / 0.9170.915 / 0.961
asciify-engine field, tone only0.581 / 0.9180.783 / 0.8890.875 / 0.954
2025.unseen.co ASCII pass0.533 / 0.8120.759 / 0.6740.870 / 0.814
webgl-ascii-hero0.479 / 0.6450.772 / 0.6880.885 / 0.842

All are measured with white characters on dark at the same grid. The studio shaders pick a character by the brightness of one pixel, so they alias and do not follow edges. The field averages each cell with a 24-step charset measured by ink, and with sample: 'shape' it also reads each cell in 3 by 5 parts on the GPU and picks the character whose strokes sit where the picture's light does, so it is ahead on every column, type up close included.

Frame cost was measured on a live source at 1080p and 4K with 12, 4 and 1 px cells and the hover running, in an emulated GPU: the field asks for 0.6 to 0.85 times webgl-ascii-hero's work and 1.1 to 1.6 times that of the bare 2025.unseen.co pass, which samples one pixel per cell with no hover. A playing video or live canvas never makes the field wait on the GPU, and a big hover costs the same as a small one. Times on a real GPU were not measured.

What a page pays was measured on a live 1080p source in Chrome. Each renderer ran in a fresh page for 5 seconds over 3 runs. Main-thread CPU time comes from Chrome's own trace, and memory counts every Chrome process (GPU and renderer included). With worker: true the field draws in a worker, so the page's main thread is barely busier than when it draws nothing at all. Measured 5 more times at 1080p and at 640 by 360, the worker field took less of the main thread than the rebuilt 2025.unseen.co pass on every run.

Live 1080p sourceMemory, all of ChromeMain thread a frame
The page alone, nothing drawn345 MB0.32 ms
asciify-engine field, still image382 MB0.33 ms
asciify-engine field in a worker400 MB0.51 ms
2025.unseen.co ASCII pass479 MB0.57 ms
webgl-ascii-hero423 MB0.96 ms
ASCII Magic, convert and draw379 MB17.6 ms

A still field draws again only when something changes, so once drawn it costs no more CPU than the bare page. ASCII Magic uses a little less memory because it draws on a 2D canvas with no WebGL context, but takes 17.6 ms of main thread per frame. 48 mounts and removals of live 1080p fields level off after the first round, so nothing leaks.

In the terminal

The asciify-engine command against jp2a and chafa, the terminal tools most people reach for. Each was asked for exactly the same columns and rows of plain characters, light on dark, from the same PNG: 20 photos the engine was not tuned on, line art and type. The scoring is the same as above.

CommandPhotosLine artType
asciify-engine (shape)0.593 / 0.8780.846 / 0.8760.902 / 0.889
asciify-engine --tone0.577 / 0.8830.812 / 0.9160.896 / 0.910
jp2a 1.10.493 / 0.7950.745 / 0.6740.870 / 0.828
chafa 1.14, ASCII symbols0.445 / 0.7000.793 / 0.7420.899 / 0.872

One of the engine's two modes scores best on every picture, up close and at a distance (44 of 44). Chafa's real strength is colour and terminal graphics protocols such as sixel and kitty, which this table does not measure.

cloudflare.com

cloudflare.com's home hero, the orange field of small ticks with a warm glow, is a pre-rendered video: 2560 by 1600, 8 seconds, 5.08 MB. asciify-engine draws the same hero live, one tick per cell lifted by a four-line custom effect, and matches a frame of the video at a distance (SSIM 0.998) from 43 kB of code and no media, at any resolution, with cursor responses and a still under reduced motion.

How it was measured

Every renderer turns the picture into the same grid (100 columns, a 1.93 cell) of light characters on a dark ground. Text output is drawn back in one font, so only the choice of characters differs; both the drawing and the picture are blurred to a viewing distance, averaging light as the eye does, and compared with SSIM. Speed is the median of seven 1920×1080 conversions to 160 columns on one core; flicker is measured on a 24-frame pan. The script, the raw numbers and the images are in the engine's bench/ and docs/benchmarks/.

Where it is not ahead

On photographs seen from a distance its lead over aalib is small (0.879 on the CPU and 0.884 on the GPU, against 0.868), and on 4 of the 20 photos aalib is better on at least one measure. On line art at a distance Devault's edge characters come close (0.871 against 0.876). On screen the GPU field leads on speed and on every quality measure. Where a page's content policy blocks Blob workers, the GPU converter runs on the page instead, at 1.1 to 2.2 ms a frame, close to ASCII Magic's. With its still-image defaults the engine changes more characters between similar frames than a plain ramp (25 %); its video settings bring that down to 3.8 %, below a plain ramp's 4.7 %. A thin horizontal or vertical line that falls on a cell edge can come out as ;:;: or ) where aalib draws dots or !. Glyph shapes are measured from DejaVu Sans Mono, so the art matches most closely in that family and its relatives. There is no ASCII renderer from Cloudflare itself; ascii.rest, which it sponsors, is measured instead.