ASCII art with ditherlab
Hand-typed art fails predictably: guessed densities, ignored 1:2 cell aspect, no light direction. ditherlab dithers real pixels into characters (so the art is right by construction), renders 3D primitives, decodes art back to images, and carries themes — named looks with settings and hand-drawing rules that you can apply and extend. Use the tool for pictures; use the references for craft; save what works.
Persistence
ACTIVE EVERY RESPONSE once triggered. Still active if unsure. Off only when the user says "stop ascii-art" or "normal mode".
Setup
ditherlab --help. Not on PATH? node <clone>/bin/ditherlab.js, or clone
https://github.com/daronthedragon/ditherlab (zero deps, Node 18+, npm link).
MCP instead of shell: ditherlab agents mcp <client> prints the config for
any MCP client (cursor, windsurf, cline, roo, vscode, zed, gemini, codex,
claude — for Claude: claude mcp add ditherlab -- node <clone>/src/mcp.js);
ditherlab agents install <name|all> writes these instructions where other
agents read them (AGENTS.md, .cursor/rules, .github/copilot-instructions.md, …)
→ tools render_text_art render_3d draw_scene auto_tune banner figlet diagram table tree box lint_art gallery decode_art render_art_image dither_image list_themes save_theme info, plus
resources ditherlab://references/*, ditherlab://themes, ditherlab://gallery,
ditherlab://figlet-fonts. PNG, JPEG (baseline + progressive), GIF, BMP,
PGM/PPM decode with zero dependencies; ffmpeg on PATH adds WebP/TIFF and video.
Procedure
Run every time, in order. Stop at the first step that resolves the task.
- Style named or implied? ("blueprint", "Game Boy", "sketch", "for my
README"):
ditherlab themes. Matching theme → use--theme <name>in every command below. No match → compose settings from the references, render, and after the user approves,ditherlab theme save <name> …with a--use-whennaming their phrase (references/themes.md). - Art from an image (photo, screenshot, logo):
ditherlab inspect photo.jpgfirst when unsure — it reports tones, colorfulness, transparency, edge density, and the width that fits 24/40 rows, with suggested flags. Then pick mode from the table and run — or let the tool pick:ditherlab auto photo.jpg --mode braille -w 70searches ~100 settings, scores each by how much structure survives, and prints the best art plus its command. Paste output verbatim. Add--save-theme <name> --use-when "…"when the result is worth keeping. - Banner text: classic FIGlet look (READMEs, CLI headers, MOTDs):
ditherlab figlet "ship it" -f slant(--listshows the 15 bundled fonts — standard slant small big banner shadow script mini block lean digital bubble term smslant smscript;-f path.flfloads any FIGlet font;--width 80 --centerwraps/centers). Pixel-font banner:ditherlab text "SHIP IT"(--style block|shadow|half|braille|quadrant,--frame round --title …); lowercase / a real typeface look:--font mono(8×16 glyphs; pair with--style halforbrailleto keep it short). Never hand-type block letters or FIGlet letters. - 3D / spinning / "donut" / 3D logo:
ditherlab 3d torus --rx 0.9 --ry 0.6 -w 70 --mode braille;--spin --frames 24 -o spin.gifanimates;--mode shapesketches;ditherlab 3d --text "GO" --ry 0.4extrudes letters;--extrude logo.pngextrudes a bitmap;--scene "sphere s=1.1; box op=subtract s=0.8 ry=0.6; torus y=-1.4 s=0.5"composes objects with CSG (op=union|subtract|intersect|smooth,x= y= z= rx= ry= rz= s=). Other forms: script pixels (PGM) then encode — the ten-line sphere in references/3d-and-shading.md. - "Something cool / abstract / a poster / a screensaver":
ditherlab gen attractor --seed 42 -w 70(also plant, maze --solve, truchet, plasma, automaton --rule 110, rose) — seeded, reproducible; offer two seeds. For images that must look hand-made:--mode flow(strokes wrap the forms),hatch(engraving),stipple(dots). references/generative.md. - Subject with no image ("draw a house", "a rocket", "a face"): do not
type from imagination. Describe it as geometry with the scene DSL and
render:
ditherlab draw scene.txt --mode braille -w 60(primitives:rect circle ellipse line poly text, units 0–100,fill=stroke=shade=sphere|cylinder;ditherlab draw --help-dsland--example house|rocket|face|cup|planetshow working scenes). If a real image of the subject exists, use it instead. For something tiny (≤ 8 rows), checkditherlab gallery <name|tag>for an exemplar and adapt it. Last resort: hand-draw following references/freehand.md and the theme'sfreehandrules, and say it is hand-drawn. - Motion on demand ("animate it", "zoom into it", "matrix reveal", a
README hero):
ditherlab fx photo.jpg --preset crash-zoom -o out.gif— camera moves (ken-burns, crash-zoom, dolly-in, pull-back, vertigo, handheld, keyframe paths), reveals (typewriter, scan, dissolve, matrix), layers (glitch, rain, snow, stars, fireworks),--crtphosphor exports;.castfor terminals,.giffor READMEs (references/motion.md). MCP:animate_art. - Video, webcam, GIF → animation:
ditherlab play clip.mp4 -w 80 --mode braille --fps 12 -o out.gif(or no-oto play in the terminal;--demofor a test). Web UI: drop a video / webcam → record gif. - Identify or restyle pasted art:
ditherlab decode art.txt -o out.png --scale 4(auto-detects ascii / braille / quadrant / sextant / ANSI; classic.ans/.nfoCP437 files are read transparently), look at the PNG, then re-encode with new options.ditherlab render art.txt -o art.pngmakes a picture of the art itself. - Dithered image file:
ditherlab dither in.png -p gb -o out.png(-p auto:8builds a palette from the image;unditherreverses). - Exploring by eye (a human is watching):
ditherlab tui photo.jpg— keys cycle mode/width/algorithm/tone/palette/theme live;eprints the equivalent command,qquits and prints it.ditherlab side a.txt b.txt --titles "before|after"joins two arts for comparison. - Any diagram — flowchart, sequence, state machine, timeline, mindmap,
architecture: write it as Mermaid and
ditherlab diagram d.mmd— the kind is auto-detected (graph TD,sequenceDiagramwith notes / loop / alt / activations / autonumber,stateDiagram-v2with[*],timeline,mindmap), or plaina -> b: labellines.subgraph … enddraws labeled containers. Styles:rounded double heavy asciishadow(▒ depth) +sketch(hand-drawn) +--color(per-node ANSI, terminals only). Bar/column/spark charts from data:ditherlab chart data.csv [--dir TB|--spark|--sorted]— exact eighth-block proportions. Never hand-route arrows for more than three boxes and never eyeball bar lengths (references/diagrams.md).
- Table, tree, boxed note:
ditherlab table data.csv|t.md|data.json [--style rounded|double|markdown|minimal](numbers right-align, wide characters are measured,--max-widthwraps cells);ditherlab tree outline.txt|dir|data.json(├──/└──);ditherlab box "text" --title … --style … --align center. - Before shipping anything hand-drawn or pasted:
ditherlab lint art.txt— tabs, trailing space, BOM, zero-width and double-width characters, ragged framed rows, box-drawing junctions that do not connect.--fixapplies the safe repairs (--padequalizes widths,--junctionsrecomputes corners/tees);--unicode/--asciiconvert line art between+-|and box-drawing characters. Exit code 1 means fix it before pasting. - Nothing to hand:
ditherlab demo [mode].
Picking mode and width
| Destination / intent | Mode | Width |
|---|---|---|
| README / markdown | ascii --theme readme (braille if Unicode is fine) | ≤ 80 |
| Terminal, a human looking | ditherlab show (real pixels via kitty/iTerm2/sixel when ditherlab term says so), else symbols | terminal width |
| Terminal, MOTD, text required | symbols (highest fidelity: per-cell shapes + fg/bg color), braille (mono detail), symbols --mono | 80–140 |
| Chat | braille or quadrant | 40–70 |
| Plain-text only | ascii -c classic | ≤ 72 |
| Sketch / line art | shape (stroke matching) or edge; ascii --edges | 60–100 |
| Retro / pixel | quadrant, or `ansi -p gb | pico8 |
| Shareable image of text | any mode + `-o art.svg | .html |
| Terminal recording / README embed | play … -o out.cast (asciinema) or -o out.gif | 60–100 |
| Subject from description | draw (2D scene) or 3d --scene/--text → any mode | 50–90 |
| Tiny icon / mascot | gallery <name> exemplar, adapted | ≤ 12 |
| Flowchart / pipeline / states | diagram (Mermaid or a -> b), --style ascii for plain text | ≤ 100 |
| Table / tree / boxed note | table / tree / box; --style markdown for GitHub-rendered docs | ≤ 100 |
| CLI / README header | `figlet -f slant | small |
Logos and icons: --trim drops uniform padding, --bg #fff composites
transparency over the page color before rendering. Older terminals and CI
logs: --colors 256 or --colors 16 re-quantizes truecolor escapes. Height
budget: --rows N shrinks the width until the art fits N lines —
use it whenever the destination has a known height (a 24-row terminal, a
chat bubble, a README section). Tune, in order: --levels for hazy or murky
photos (auto-levels), --clahe 0.4 for detail hidden in shadows/skies,
--contrast 0.2 flat → punchier; --sharpen 0.6 mushy →
crisp; --invert tones backwards; --paper light background; -a atkinson
sparse/clean, -a bayer4 retro, -a bluenoise grain without pattern;
--strength 0.5 calms a busy dither; --frame round --title when the art
sits in prose; --linear (gamma-correct: mid-gray comes out ~21% ink, the
true luminance, instead of 50%) when tones look too dark or too bright;
--oklab for palette work (-p gb|cga|auto:N) so colors map by perceived
distance. Read references/ascii-fundamentals.md before any hand
adjustment; references/3d-and-shading.md for light and form.
Rules
- Never present hand-typed art as tool output. Anything beyond a 4-line
doodle is produced by a ditherlab command shown in the transcript, or is
explicitly labeled hand-drawn, follows references/freehand.md, and passed
ditherlab lint(exit 0) before it was pasted. - Flowcharts, tables, trees and banners come from
diagram,table,tree,figlet— not from hand-aligned characters. - Paste tool output verbatim; never retouch characters.
- Look at the result before sending, and measure it:
ditherlab score art.txt image.png(or MCPscore_art) returns 0..1 structure preserved — below 0.75 means rerun withauto, a wider width, orsymbols/braille. This works for hand-drawn art against a reference image too. - ANSI escapes go only to terminals; files and markdown get
ascii,braille,blocks, or an exported.svg/.html/.png. - Respect the width column; wider than the medium wraps into noise.
- A look the user approves gets saved as a theme before the turn ends.
- When settings are in doubt, run
autobefore fiddling by hand; when the subject is in doubt, describe geometry (draw,3d --scene) before typing characters. - Exact-font output: if the destination font matters, export a glyph atlas
from the web UI (download ▾ → glyph atlas) and
ditherlab font <file>;shapeandrenderthen match that font's real glyphs.
Example
Instead of:
Here's a sphere for your README:
___
/ \
| ( ) |
\ ___ /
Do:
$ ditherlab 3d sphere -w 46 --theme readme
[paste of the actual command output]
References (read when relevant, not all at once)
references/ascii-fundamentals.md— cell aspect, measured glyph density, stroke vocabulary, Unicode families, medium limits, composition.references/3d-and-shading.md—ditherlab 3d, the PGM compute-first method, isometric/perspective rules, shading order, materials, animation.references/freehand.md— grid passes, block letters, frames, tiny icons, diagrams, failure table.references/themes.md— schema, resolution order,theme save, config defaults, style-by-name workflow.references/terminal-graphics.md—term/show: kitty, iTerm2, sixel protocols, when pixels beat characters, reading CP437 ANSI art.references/generative.md— flow/hatch/stipple modes,genkinds, seeds, recipes for posters and textures.references/motion.md—fx: cameras, reveals, particle layers, CRT, keyframe paths, duration taste per medium, the Director tab.references/diagrams.md—diagram: the Mermaid subset anda -> bsyntax, styles, directions, how layout and routing work, what to do when a chart is too wide;table,tree,box,figlet,lint.gallery.json(ditherlab gallery) — 30 exemplars with proportion/stroke notes: animals, objects, symbols, UI sets, shaded sphere, isometric cube.scenes:ditherlab draw --example …— five worked scene files to copy.
Observable effect
Every piece of art in a reply is preceded by the ditherlab command (or MCP
call) that produced it — auto when settings were uncertain, draw/3d
when the subject had no image, diagram/table/tree/figlet for
structured text — or is labeled hand-drawn with the gallery piece / theme /
reference it followed and a lint result; doubtful results show a
score; pasted art gets a decode; a liked look ends in theme save.
The eval counts exactly this.