ASCII Art Renderer
Turns a real picture into ASCII characters in the browser: each shrunken pixel becomes a more or less dense letter, in a glowing green that gently pulses.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Distortion has 31 effects, including 5 free. Explore the category →
3 usage examples



How it works
You place your picture in .ascg-frame as an <img class="ascg-src"> (or a data-src). On load, the script draws it very small on an off-screen canvas: one pixel per character. The column count comes from data-cols (auto by default: as many characters as the frame width holds); the row count follows the picture's proportions and the real size of a character, measured in the font in use.
getImageData() then reads every pixel. Its luminance (0.2126 R + 0.7152 G + 0.0722 B, multiplied by opacity) picks a character from the data-chars ramp, darkest to brightest: " .:-=+*#%@" by default. A black pixel gives a space, a white one an @. The resulting text fills <pre class="ascg-art">, one line per <span>, and the original picture is hidden.
The green glow of each line pulses over 2 s (ascgGlow animation), every other line half a second later. If the frame width changes, a ResizeObserver runs the conversion again (with data-cols="auto" only); if you change the picture's src, it is converted again once loaded. The demo code holds a cat head as an SVG data: URL, to be replaced with your picture.
Accessibility
- Alternative text: the
<pre>hasrole="img"and receives your picture'saltas itsaria-label: a screen reader announces "Cat head", not hundreds of symbols. So write analtthat describes the picture. - Reduced motion: under
prefers-reduced-motion: reduce, the glow stops pulsing; the drawing stays on screen with a steady glow. The conversion itself is not an animation. - Without JavaScript or with an unreadable picture: the original picture simply stays visible in the frame. The same happens with a picture from another domain served without CORS permission, which the browser refuses to read.
- Readability: the drawing is decorative by nature; do not put text the visitor must read inside the picture, it becomes unreadable once converted.
Browser compatibility
2D canvas, getImageData, ResizeObserver and 3.2 KB of JavaScript, no dependency. The most recent feature used is ResizeObserver (Chrome 64, Firefox 69, Safari 13.1 according to MDN data); without it, the conversion happens on load but does not follow width changes. Measured in Chromium 151, Firefox 153 and WebKit 26.5: identical conversion (an SVG data: picture is readable in all three), two independent copies, nothing on window, no off-screen work.
Without JavaScript, the original picture shows inside the black frame. A picture from another domain without a CORS header (or without crossorigin="anonymous") cannot be read by the canvas: it stays visible as is, with no error.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0273-root" data-cols="auto">
<div class="ascg-frame">
<img class="ascg-src" alt="Cat head" src="your-image.jpg">
<pre class="ascg-art" role="img"></pre>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Your picture (HTML — <img class="ascg-src" src="…" alt="…"> or data-src) |
Demo cat head SVG | A same-origin picture, a data: URL, or one from a server that allows CORS (then add crossorigin="anonymous"). High-contrast pictures on a dark background work best. |
Columns (HTML — data-cols on the root) |
auto (frame width) |
A fixed number (40, 80, 120…) gives the same detail everywhere; height follows the picture's proportions. Clamped between 8 and 240. |
Character ramp (HTML — data-chars) |
" .:-=+*#%@" |
Darkest to brightest. " ░▒▓█" gives solid blocks, " 01" a binary look. |
Invert (HTML — data-invert="true") |
No | For a dark subject on a light background, or to show dark characters on a light frame. |
Detail (CSS — --ascg-size, --ascg-spacing) |
10 px, 1 px spacing | 4 to 6 px with --ascg-spacing: 0 for a detailed portrait; 12 px and up for a chunky look. |
Colours (CSS — --ascg-color, --ascg-glow, --ascg-border) |
#3f3, 51,255,51, #0f0 |
--ascg-glow is written as three r,g,b numbers. Amber, blue or white change the mood completely. |
Frame (CSS — --ascg-w, --ascg-h, --ascg-page, --ascg-root-h) |
280 px × 140 px minimum, #0a0a0f background |
--ascg-w: 100% with data-cols="auto" fills a whole area; --ascg-page: transparent and --ascg-root-h: 0 remove the demo frame. |
FAQ
data: URL, or from a server that sends Access-Control-Allow-Origin, and add crossorigin="anonymous" on the <img> tag.auto mode. Nothing runs continuously; only the glow is animated in CSS.src changes. A video would need the canvas to be read again on every frame, which the effect does not do.