Distortion✨ Premium

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.

CSSMonospaceTerminal

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

Open-source project mascot drawn in characters — ASCII Art Renderer example 1

① Open-source project mascot drawn in characters

WhenOn the page of a small command-line tool, the mascot illustration (a fox) is converted into orange ASCII inside a frame, next to a sample command and the contributor count.
WhyA project that lives in the terminal introduces itself with a picture made of characters: the mascot stays recognisable and visitors grasp the project's world at once.
Settingsdata-cols="auto", --ascg-w: 230px, --ascg-size: 6px, --ascg-spacing: 0, --ascg-color: #ffb86b, --ascg-glow: 255,170,90, --ascg-border: #3d2a14. Source picture: a demo SVG as a data: URL.
Landscape converted into a full-width background for a conference page — ASCII Art Renderer example 2

② Landscape converted into a full-width background for a conference page

WhenAt the top of a page for an evening of terminal talks, a moonlit mountain landscape is converted into blue characters across the whole width; title, date and booking button sit in an opaque panel.
WhyIn auto mode the conversion fills the whole width: you get a textured background that is still a real picture, with no extra file to prepare. The opaque panel keeps the text readable.
SettingsRoot in position: absolute; inset: 0, --ascg-w: 100%, --ascg-h: 100%, --ascg-size: 7px, --ascg-spacing: 0, --ascg-border: transparent, colour #9fd3ff; the scene removes the frame's radius and padding.
Character avatar preview after picking a picture — ASCII Art Renderer example 3

③ Character avatar preview after picking a picture

WhenIn the settings of a list app, the member clicks one of three thumbnails: the avatar on their profile card is converted again into green characters inside a round badge.
WhyChanging the picture's src is enough: the effect converts again on load. The profile keeps a consistent look, whatever picture is chosen.
Settings--ascg-w: 150px, --ascg-size: 4px, --ascg-spacing: 0; the scene script only swaps the src and alt of .ascg-src when a thumbnail is clicked. Round badge made by the scene (border-radius: 50% on the frame).

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> has role="img" and receives your picture's alt as its aria-label: a screen reader announces "Cat head", not hundreds of symbols. So write an alt that 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.

Chrome 64+✓ Full (measured)
Firefox 69+✓ Full (measured)
Safari 13.1+✓ Full (measured in WebKit)
Edge 79+✓ Full
Mobile iOS 13.4+✓ Full
Android Chrome 64+✓ Full

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):

index.html — structure
<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 / propertyDefaultEffect
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

The browser forbids the canvas from reading a picture from another domain without CORS permission. Serve the picture from your site, as a data: URL, or from a server that sends Access-Control-Allow-Origin, and add crossorigin="anonymous" on the <img> tag.
No. The picture is shrunk to one cell per character (for instance 60 × 30 pixels) before being read: a few thousand pixels, once on load, then on each frame width change in auto mode. Nothing runs continuously; only the glow is animated in CSS.
Not with this code: it converts a still picture, on load and when its src changes. A video would need the canvas to be read again on every frame, which the effect does not do.